if else conditions not working in ejs after I hosted using aws and nginx - ejs

if else conditions working fine in the localhost but not working after I hosted.
const productDetails = async (req, res) => {
try {
userSession = req.session;
const id = req.query.id;
const products = await Product.find();
const productData = await Product.findById({ _id: id });
if (productData) {
res.render("singleProduct", {
isLoggedin,
id: userSession.userId,
product: productData,
products: products,
});
} else {
res.redirect("/home");
}
} catch (error) {
console.log(error.message);
}
};
<% if(id) { %>
<%- include('../partials/userLoggedinHeader') %>
<% }else { %>
<%- include('../partials/userHeader') %>
<% } %>
<% if(id){ %>
<a class="primary-btn" onclick="addToCart('<%=product._id%>')">Add to Cart</a>
<a class="icon_btn" onclick="addToWishlist('<%=product._id%>')"><i class="lnr lnr lnr-heart"></i></a>
<% }else {%>
<a class="primary-btn" href="/addToCart?id=<%=product._id%>">Add to Cart</a>
<a class="icon_btn" href="/AddToWishlist?id=<%=product._id%>"><i class="lnr lnr lnr-heart"></i></a>
<% } %>
here I have rendered id and it is working fine in localhost but the second 'if' not working after I hosted using aws and nginx. Can someone tell me what's happening here?

Have you checked to see what version of node is installed on the aws instance? That could be causing some of your problems if it isn't the same as the one you are using on your computer

Related

Cannot access 'getSession' before initialization: How to resolve circular dependencies in SWR/Passport.js?

I'm trying to implement MongoDB, Next.JS and Passport.js, However upon starting my app and while trying to login, I get this 500 status error:
Cannot access 'getSession' before initialization
The preamble for all this is I am using a hook which calls/api/user to see if the req.user has been set by passport thus creating the 'user' below. And if one exists you get access to the other links/routes in the app.
This is the function declaration regarding the getSession function in the session.js file. So I suspect this is what is causing the circular dependencies issue, naturally.
import MongoStore from "connect-mongo";
import nextSession from 'next-session';
import { getMongoClient } from "./mongodb";
const mongoStore = MongoStore.create({
clientPromise: getMongoClient(),
stringify: false,
});
const getSession = nextSession({
store: mongoStore,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
maxAge: 2 * 7 * 24 * 60 * 60, // 2 weeks,
path: "/",
sameSite: "strict",
},
touchAfter: 1 * 7 * 24 * 60 * 60, // 1 week
});
export default function session(req, res, next) {
getSession(req, res);
next();
}
And this is the auth file with the passport file, passport initialization and session.
import passport from '../lib/passport'
import session from '../lib/session'
const auths = [session, passport.initialize(), passport.session()];
export default auths;
/component/Layout
export default function Layout({ children, showFooter = false }) {
const [user, { mutate }] = useCurrentUser();
async function handleLogout() {
axios
.get('/api/logout').then(() => {
mutate({ user: null })
Router.push('/',)
}).catch(err => console.log('err', err))
}
return <>
<div className="shadow bg-base-200 drawer h-screen">
<div className="flex-none hidden lg:block">
<ul className="menu horizontal">
{user
?
<>
<li>
<Link href="/profile">
<a>Profile</a>
</Link>
</li>
<li>
<Link href="/dashboard">
<a>Dashboard</a>
</Link>
</li>
<li>
<a role="button" onClick={handleLogout}>
Logout
</a>
</li>
<li className="avatar">
<div className="rounded-full w-10 h-10 m-1">
<img src="https://i.pravatar.cc/500?img=32" />
</div>
</li></>
:
<>
<li>
<Link href="/login">
<a>Login</a>
</Link>
</li>
<li>
<Link href="/registration">
<a>Register</a>
</Link>
</li>
</>
}
</ul>
</div>
</div>
</>;
}
This is the hook itself:
import useSWR from 'swr';
export const fetcher = (url) => {
try {
return fetch(url).then((res) => {
console.log('res', res)
return res.json()
})
} catch (error) {
console.log('error', error)
}
}
export function useCurrentUser() {
const { data, mutate } = useSWR('/api/user', fetcher);
const user = data?.user;
return [user, { mutate }];
}
export function useUser(id) {
const { data } = useSWR(`/api/users/${id}`, fetcher, {
revalidateOnFocus: false,
});
return data?.user;
}
And this is the api: /api/user:
import nextConnect from 'next-connect'
import auth from '/middleware/auth'
const handler = nextConnect()
handler
.use(auth)
.get((req, res) => {
console.log("req.user ", req.user);
if (req.user) {
res.json({ user: req.user })
} else {
res.json({ user: null })
}
})
export default handler
Also this is my repo
And this is a link to the app on vercel.

Trying to post data via html-form and JavaScript to RESTapi – gets 404 Cannot Post?

I have googled a lot, gone through a lot of questions but can't find an answer.
I have built a simple RESTapi with node and mongoDB, using express and mongoose. The database is hosted on Atlas. The RESTapi works fine when accessing with postman, no problem there.
To access and use the RESTapi via the site I get the GET and DELETE method to work, but when trying to post data with a form I get the error “Cannot Post/ 404”. I have tried a lot of things but can´t get it to work. (I don't know it it is related, but the content-security policies which makes some scripts don't load, I have tried to allow everything in the head meta-info in index.html, but it doesn't make a change)
Request headers
Accept
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding
gzip, deflate
Accept-Language
en-US,en;q=0.5
Cache-Control
no-cache
Connection
keep-alive
Content-Length
530
Content-Type
multipart/form-data; boundary=---------------------------52045656921129358052645853016
Host
localhost:3000
Origin
http://localhost:3000
Pragma
no-cache
Referer
http://localhost:3000/
Upgrade-Insecure-Requests
1
The RESTapi and the site accessing is in the same folder, here is the project structure:
Here is the code:
js/main.js
window.onload = loadCourses();
// Variebles from the form
let formCreate = document.getElementById("formCreate");
let courseIdIn = document.getElementById("courseId");
let courseNameIn = document.getElementById("courseName");
let coursePeriodIn = document.getElementById("coursePeriod");
let message_form = document.getElementById("message_form");
const myForm = document.getElementById('formCreate');
myForm.addEventListener('submit', (e) => {
console.log('Hello from eventlistner');
e.preventDefault();
addCourse();
})
// GET courses
function loadCourses() {
$.getJSON("http://localhost:3000/courses", function(data) {
//rensa listan
console.log(data);
$("#tbody").html("");
for(let i = 0; i<data.length; i++) {
$("tbody").append("<tr><td>" + data[i]._id + "</td>" + "<td>"+ data[i].courseId + "</td>" + "<td>" + data[i].courseName +
"</td>" + "<td>" + data[i].coursePeriod + "</td>" + "<td><img class='deleteSize' onclick='deleteCourse(\""+data[i]._id+"\")' src='images/delete-photo.svg'alt='ikon radare'></td></tr>");
}
});
}
// DELETE course
function deleteCourse(id) {
console.log(id)
$.ajax({
type: "DELETE",
url: "http://localhost:3000/courses/" + id
}).done(function(response) {
console.log(response);
//ladda om listan
loadCourses();
});
}
// add course
function addCourse() {
console.log("Hi from add Course");
let courseIdEl = courseIdIn .value;
let courseNameEl = courseNameIn.value;
let coursePeriodEl = coursePeriodIn.value;
let courseObj =
{
"courseId": courseIdEl.value,
"courseName": courseNameEl.value,
"coursePeriod": coursePeriodEl.value
}
console.log(courseObj);
//Skapar fetch-anrop
fetch('http://localhost:3000/courses', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': '*/*',
},
body: JSON.stringify(courseObj)
})
.then(response => response.json())
.then(data => {
// message
let message = data.message;
message_form.innerHTML = message;
//document.getElementById("message_form").innerHTML = message;
loadCourses();
formCreate.reset();
})
.catch(error => {
console.log('Error: ', error);
})
}
the index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-policy" content="default-src *;
script-src *;
connect-src *;">
<link rel="stylesheet" href="css/style.css">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="js/main.js"></script>
<script src="main2.js"></script>
<title>Moment 3 - mongoose.js</title>
</head>
<body>
<h1>Moment 3 - mongoose.js</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Kurs</th>
<th>Kursnamn</th>
<th>Period</th>
<th>Radera</th>
</tr>
</thead>
<tbody id="tbody">
<tr>
<td>1</td>
<td>DT162G</td>
<td>JavaScript-basar webbutveckling</td>
<td>1</td>
<td><img class="deleteSize" onclick="deleteCourse()" src="images/delete-photo.svg" alt="ikon radare">
</td>
</tr>
</tbody>
</table>
<h3>Create course:</h3>
<form class="forms" action="" id="formCreate" method="POST" enctype="multipart/form-data">
<!--fält för formulär, hela den grå delen-->
<fieldset id="field">
<p class="pfield" id="message_form"></p>
<label for="courseId">Kurskod:</label><br>
<input type="text" name="courseId" id="courseId" class="input">
<br>
<label for="courseName">Kursnamn:</label><br>
<input type="text" name="courseName" id="courseName" class="input">
<br>
<label for="coursePeriod">Kursperiod:</label><br>
<input type="number" id="coursePeriod" name="coursePeriod" min="1" max="2">
<div class="btn-wrapper">
<button type="submit" name="submitPost" id="btn-create" class="btn btn2">Publish</button>
<button type="reset" name="deletePost" id="btn-reset" class="btn btn2 btn-reset">Delete
field</button>
</div>
</fieldset>
</form>
</body>
</html>
RESTapi code
routes/courses.js
const express = require('express');
const router = express.Router();
// Hämtar schemamodel
const Courses = require('../models/CourseModel');
// Get all courses
router.get('/', async (req, res) => {
try {
const allCourses = await Courses.find();
if(!allCourses) {
throw Error('No items found');
} else {
res.status(200).json(allCourses);
}
} catch(err) {
res.status(500).json( {msg: err})
}
})
// GET one course
router.get('/:id', getCourse, (req, res) => {
res.json(res.course)
})
// Create course
router.post('/', async (req, res) => {
const newCourse = new Courses({
courseName: req.body.courseName,
courseId: req.body.courseId,
coursePeriod: req.body.coursePeriod
});
try {
const course = await newCourse.save();
if(!course) {
throw Error('Something went wrong while saving the post =( ');
} else {
// It worked ok, post is created
res.status(201).json(course);
}
} catch (err) {
// bad input from user = 400
res.status(400).json( {msg: err})
}
});
// UPDATE one course
router.patch('/:id', getCourse, async (req, res) => {
// options new = true makes mangoose send back updated data and not old
let options = { new: true };
try {
const course = await Courses.findByIdAndUpdate(req.params.id, req.body, options);
if(!course) {
throw Error ('Something went wrong while updating the post =( ');
} else {
// It worked ok, post is created
res.json(course).status(201).json( {success: true});
}
} catch {
res.status(400).json( {message: err.message})
}
})
// DELETE one course
router.delete('/:id', getCourse, async (req, res) => {
try {
await res.course.deleteOne();
res.status(200).json( {message: 'Success: Course is deleted!'})
} catch (err){
res.status(503).json( {message: err.message})
}
})
// Creating middlewhere function to re-use, findbyid. Middlewhere idé = webdev simplified
async function getCourse(req, res, next) {
let course;
try {
course = await Courses.findById(req.params.id)
if (course == null) {
return res.status(404).json( {message: 'Cant find any course with that ID'})
}
} catch (err) {
return res.status(500).json( {message: err.message})
}
res.course = course;
next();
}
module.exports = router;
models/CourseModel.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CourseSchema = new Schema( {
courseName: {
type: String,
required: true
},
courseId: {
type: String,
required: true
},
coursePeriod: {
type: Number,
required: true
}
});
module.exports = mongoose.model('Courses', CourseSchema );
server.js
require('dotenv').config();
const express = require('express');
const app = express();
const path = require("path");
const mongoose = require('mongoose');
//const { MONGO_URI } = require('./config');
// Connect to MongoDB
mongoose.connect(process.env.MONGO_URI,{ useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false })
.then(() => console.log('Connected to Mongo Database.'))
.catch(err => console.log(err));
//BodyParser Middleware, for use of JSON in body
app.use(express.json());
// skapa statisk sökväg
app.use(express.static(path.join(__dirname, 'public')));
// Routes
const courseRoutes = require('./routes/courses.js')
app.use('/courses', courseRoutes)
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log (`Server run at port ${PORT}`));
well, it was a stupid mistake in HTML, the script source tag for the javascript-file main.js was in the head section. Of course, it must be in the bottom just before the body-tag. So stupid of me.

Passing parameters to MVC Ajax.ActionLink

How can I send the value of the TextBox as a parameter of the ActionLink?
I need to use the Html.TextBoxFor
<%= Html.TextBoxFor(m => m.SomeField)%>
<%= Ajax.ActionLink("Link Text", "MyAction", "MyController", new { foo = "I need here the content of the textBox, I mean the 'SomeField' value"}, new AjaxOptions{ UpdateTargetId = "updateTargetId"} )%>
The Contoller/Actions looks like this:
public class MyController{
public ActionResult MyAction(string foo)
{
/* return your content */
}
}
Using MVC 2.0
How can I send the value of the TextBox as a parameter of the ActionLink?
The semantically correct way of sending input fields values (such as textboxes) to a server is by using an html <form> and not links:
<% using (Ajax.BeginForm("MyAction", "MyController", new AjaxOptions { UpdateTargetId = "updateTargetId" })) { %>
<%= Html.TextBoxFor(m => m.SomeField) %>
<input type="submit" value="Link Text" />
<% } %>
Now in your controller action you will automatically get the value of the SomeField input entered by the user:
public class MyController: Controller
{
public ActionResult MyAction(string someField)
{
/* return your content */
}
}
You could of course try to violate the markup semantics and the way HTML is supposed to work by insisting on using an ActionLink even if it is wrong. In this case here's what you could do:
<%= Html.TextBoxFor(m => m.SomeField) %>
<%= Html.ActionLink("Link Text", "MyAction", "MyController", null, new { id = "myLink" }) %>
and then in a separate javascript file unobtrusively AJAXify this link using jQuery:
$(function() {
$('#myLink').click(function() {
var value = $('#SomeField').val();
$('#updateTargetId').load(this.href, { someField: value });
return false;
});
});

Images being displayed as weird characters

In this project I'm trying to implement a price list (it's a taxidermy site) So here's what I'm going to do, show you the code
PriceListController
public partial class PriceListController : Controller
{
public PriceListController()
{
}
[CanonicalUrlAttribute("PriceList")]
[CompressionFilter(Order = 1)]
[CacheFilter(Duration = 120, Order = 1)]
public virtual ActionResult Index()
{
GodsCreationTaxidermyEntities context = new GodsCreationTaxidermyEntities();
var viewModel = new PriceListViewModel() { PriceListAnimals = context.GetAnimalListForPriceList() };
return View(viewModel);
}
[CompressionFilter(Order = 1)]
[CacheFilter(Duration = 120, Order = 2)]
public virtual ActionResult List(string animal)
{
GodsCreationTaxidermyEntities context = new GodsCreationTaxidermyEntities();
var viewModel = new PriceListIndexViewModel() { AnimalPrices = context.GetPriceListByAnimal(animal) };
return View(viewModel);
}
}
The index work fine. Here's Index.aspx
Index.aspx
<div id="main-content" title="AnimalBox" style="float:none;">
<%--<%= Html.DropDownList("AnimalList", Model.Animals, "[Select One]", new { #class = "inputDropDown" })%>--%>
<% Html.DataList(Model.PriceListAnimals).Columns(6).Item(item =>
{
item.Template(galleryImage =>
{%>
<div style="margin-right:45px; line-height:150%;">
<span><%= Html.ActionLink(galleryImage.AnimalName,"List",new { #animal = galleryImage.AnimalName }) %></span>
</div>
<% });
}).Render(); %>
</div>
Now we move on to the listing page
List.aspx
<div class="maintext" id="pricelist">
<h2 class="sectionHeader">:: Gods Creation Taxidermy : PriceList ::</h2>
<% Html.DataList(Model.AnimalPrices).Columns(7).Item(item =>
{
item.Template(galleryImage =>
{%>
<div><%=galleryImage.TypeName %></div>
<%});
}).Render(); %>
<% Html.DataList(Model.AnimalPrices).Columns(7).Item(item2 =>
{
item2.Template(galleryImage =>
{%>
<div><%=galleryImage.MountPrice %></div>
<% });
}).Render(); %>
</div>
Here's a screenshot of how this is beeing displayed
Screenshot
If anyone couod hekp I'd surely be grately.
EDIT: By the way the images arent being stored in a DB, just the path, which makes this even more fonfusing.
This issue is resolved, I have a couple things backwards

jquery.validate lost on ajax replacement and only showing last error

I am using jquery.validate in MVC 2 with MicrosoftMvcJQueryValidation. I have data annotations on my model which is then being translated into jquery validators. I am using a modification to MicrosoftMvcJQueryValidation as outlined by Soe Tun to allow my error messages to appear in a validation summary instead of beside the controls.
When the page loads, everything works as expected. The problem is that I am using ajax forms with replace mode to rewrite the form. When I do this, I lose all of my client side validation.
Validation still happens server side, and the fields that have errors are correctly being given the css classes to change their style. However, only the last error message is being shown in my validation summary.
The controller isn't anything special. If the model is valid, do work, otherwise return the same model back into the view.
Here's a sample of my ajax form
<% using (Ajax.BeginForm("AddCreditCard", "Dashboard",
new { },
new AjaxOptions() {
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "quickpay-wrapper",
OnSuccess = "newPaymentSetup",
LoadingElementId = "loading-pane"
}, new { id="new-credit-card-form" })) { %>
Here is the modified javascript.
jQuery.validator.addMethod("regex", function(value, element, params) {
if (this.optional(element)) {
return true;
}
var match = new RegExp(params).exec(value);
return (match && (match.index == 0) && (match[0].length == value.length));
});
// glue
function __MVC_ApplyValidator_Range(object, min, max) {
object["range"] = [min, max];
}
function __MVC_ApplyValidator_RegularExpression(object, pattern) {
object["regex"] = pattern;
}
function __MVC_ApplyValidator_Required(object) {
object["required"] = true;
}
function __MVC_ApplyValidator_StringLength(object, maxLength) {
object["maxlength"] = maxLength;
}
function __MVC_ApplyValidator_Unknown(object, validationType, validationParameters) {
object[validationType] = validationParameters;
}
function __MVC_CreateFieldToValidationMessageMapping(validationFields) {
var mapping = {};
for (var i = 0; i < validationFields.length; i++) {
var thisField = validationFields[i];
mapping[thisField.FieldName] = "#" + thisField.ValidationMessageId;
}
return mapping;
}
function __MVC_CreateErrorMessagesObject(validationFields) {
var messagesObj = {};
for (var i = 0; i < validationFields.length; i++) {
var thisField = validationFields[i];
var thisFieldMessages = {};
messagesObj[thisField.FieldName] = thisFieldMessages;
var validationRules = thisField.ValidationRules;
for (var j = 0; j < validationRules.length; j++) {
var thisRule = validationRules[j];
if (thisRule.ErrorMessage) {
var jQueryValidationType = thisRule.ValidationType;
switch (thisRule.ValidationType) {
case "regularExpression":
jQueryValidationType = "regex";
break;
case "stringLength":
jQueryValidationType = "maxlength";
break;
}
thisFieldMessages[jQueryValidationType] = thisRule.ErrorMessage;
}
}
}
return messagesObj;
}
function __MVC_CreateRulesForField(validationField) {
var validationRules = validationField.ValidationRules;
// hook each rule into jquery
var rulesObj = {};
for (var i = 0; i < validationRules.length; i++) {
var thisRule = validationRules[i];
switch (thisRule.ValidationType) {
case "range":
__MVC_ApplyValidator_Range(rulesObj,
thisRule.ValidationParameters["minimum"], thisRule.ValidationParameters["maximum"]);
break;
case "regularExpression":
__MVC_ApplyValidator_RegularExpression(rulesObj,
thisRule.ValidationParameters["pattern"]);
break;
case "required":
var fieldName = validationField.FieldName.replace(".", "_");
if ($("#" + fieldName).get(0).type !== 'checkbox') {
// only apply required if the input control is NOT a checkbox.
__MVC_ApplyValidator_Required(rulesObj);
}
break;
case "stringLength":
__MVC_ApplyValidator_StringLength(rulesObj,
thisRule.ValidationParameters["maximumLength"]);
break;
default:
__MVC_ApplyValidator_Unknown(rulesObj,
thisRule.ValidationType, thisRule.ValidationParameters);
break;
}
}
return rulesObj;
}
function __MVC_CreateValidationOptions(validationFields) {
var rulesObj = {};
for (var i = 0; i < validationFields.length; i++) {
var validationField = validationFields[i];
var fieldName = validationField.FieldName;
rulesObj[fieldName] = __MVC_CreateRulesForField(validationField);
}
return rulesObj;
}
function __MVC_EnableClientValidation(validationContext) {
// this represents the form containing elements to be validated
var theForm = $("#" + validationContext.FormId);
var fields = validationContext.Fields;
var rulesObj = __MVC_CreateValidationOptions(fields);
var fieldToMessageMappings = __MVC_CreateFieldToValidationMessageMapping(fields);
var errorMessagesObj = __MVC_CreateErrorMessagesObject(fields);
var options = {
errorClass: "input-validation-error",
errorElement: "span",
errorPlacement: function(error, element) {
var messageSpan = fieldToMessageMappings[element.attr("name")];
$(messageSpan).empty();
$(messageSpan).removeClass("field-validation-valid");
$(messageSpan).addClass("field-validation-error");
error.removeClass("input-validation-error");
error.attr("_for_validation_message", messageSpan);
error.appendTo(messageSpan);
},
messages: errorMessagesObj,
rules: rulesObj,
success: function(label) {
var messageSpan = $(label.attr("_for_validation_message"));
$(messageSpan).empty();
$(messageSpan).addClass("field-validation-valid");
$(messageSpan).removeClass("field-validation-error");
}
};
var validationSummaryId = validationContext.ValidationSummaryId;
if (validationSummaryId) {
// insert an empty <ul> into the validation summary <div> tag (as necessary)
$("<ul />").appendTo($("#" + validationSummaryId + ":not(:has(ul:first))"));
options = {
errorContainer: "#" + validationSummaryId,
errorLabelContainer: "#" + validationSummaryId + " ul:first",
wrapper: "li",
showErrors: function(errorMap, errorList) {
var errContainer = $(this.settings.errorContainer);
var errLabelContainer = $("ul:first", errContainer);
// Add error CSS class to user-input controls with errors
for (var i = 0; this.errorList[i]; i++) {
var element = this.errorList[i].element;
var messageSpan = $(fieldToMessageMappings[element.name]);
var msgSpanHtml = messageSpan.html();
if (!msgSpanHtml || msgSpanHtml.length == 0) {
// Don't override the existing Validation Message.
// Only if it is empty, set it to an asterisk.
//messageSpan.html("*");
}
messageSpan.removeClass("field-validation-valid").addClass("field-validation-error");
$("#" + element.id).addClass("input-validation-error");
}
for (var i = 0; this.successList[i]; i++) {
// Remove error CSS class from user-input controls with zero validation errors
var element = this.successList[i];
var messageSpan = fieldToMessageMappings[element.name];
$(messageSpan).addClass("field-validation-valid").removeClass("field-validation-error");
$("#" + element.id).removeClass("input-validation-error");
}
if (this.numberOfInvalids() > 0) {
errContainer.removeClass("validation-summary-valid").addClass("validation-summary-errors");
}
this.defaultShowErrors();
// when server-side errors still exist in the Validation Summary, don't hide it
var totalErrorCount = errLabelContainer.children("li:not(:has(label))").length + this.numberOfInvalids();
if (totalErrorCount > 0) {
$(this.settings.errorContainer).css("display", "block").addClass("validation-summary-errors").removeClass("validation-summary-valid");
$(this.settings.errorLabelContainer).css("display", "block");
}
},
messages: errorMessagesObj,
rules: rulesObj
};
}
// register callbacks with our AJAX system
var formElement = document.getElementById(validationContext.FormId);
var registeredValidatorCallbacks = formElement.validationCallbacks;
if (!registeredValidatorCallbacks) {
registeredValidatorCallbacks = [];
formElement.validationCallbacks = registeredValidatorCallbacks;
}
registeredValidatorCallbacks.push(function() {
theForm.validate();
return theForm.valid();
});
theForm.validate(options);
}
// need to wait for the document to signal that it is ready
$(document).ready(function() {
var allFormOptions = window.mvcClientValidationMetadata;
if (allFormOptions) {
while (allFormOptions.length > 0) {
var thisFormOptions = allFormOptions.pop();
__MVC_EnableClientValidation(thisFormOptions);
}
}
});
I've tried moving the calls at the bottom in the document ready into my OnSuccess method, but that didn't do it.
So, how do I get client side validation to reinitialize when I do my ajax replace, and how do I get all my errors to show in the validation summary? I'm hoping that if I fix one issue, it will correct the other.
EDIT:
Here's a little more info about what I am doing
Here's the wrapper
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<QuickPayModel>" %>
<div id="quickpay-wrapper">
<% if (Model.NewPaymentMethod) { %>
<% Html.RenderAction<DashboardController>(x => x.QuickPayNewMethod()); %>
<% } else { %>
<% Html.RenderPartial("QuickPayMakePayment", Model); %>
<% } %>
</div>
Here is the make a payment panel.
<%= Html.ClientValidationSummary(new { id = "valSumContainer" })%>
<% Html.EnableClientValidation(); %>
<% using (Ajax.BeginForm("QuickPay", "Dashboard",
new { },
new AjaxOptions() {
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "quickpay-wrapper",
OnSuccess = "updatePaymentHistory",
LoadingElementId = "loading-pane"
}, new { }))
{ %>
<div class="horizontalline"><%= Html.Spacer() %></div>
<% ViewContext.FormContext.ValidationSummaryId = "valSumContainer"; %>
<p>
<%: Html.LabelFor(x => x.PaymentMethods)%>
<% if (Model.HasOnePaymentMethod) { %>
<%: Html.DisplayFor(x => x.SelectedPaymentMethodName) %>
<%: Html.HiddenFor(x => x.SelectedPaymentMethodId) %>
<% } else { %>
<%: Html.DropDownListFor(x => x.SelectedPaymentMethodId, Model.PaymentMethodsSelectList, "Select a Payment Method", new { })%>
<%: Html.HiddenFor(x => x.SelectedPaymentMethodName)%>
<script type="text/javascript">
$(function () {
$("#PaymentMethods").change(function () {
$("#SelectedPaymentMethodId").val($(this).val());
$("#SelectedPaymentMethodName").val($('option:selected', this).text());
});
});
</script>
<% } %>
<%: Html.Spacer(12, 1) %><%: Ajax.ActionLink("New Payment Method", "QuickPayNewMethod",
new AjaxOptions() { InsertionMode = InsertionMode.Replace,
UpdateTargetId = "quickpay-wrapper",
OnSuccess = "newPaymentSetup",
LoadingElementId = "loading-pane"
})%>
<%: Html.ValidationMessageFor(x => x.SelectedPaymentMethodId)%>
</p>
<p>
<%: Html.LabelFor(x => x.Amount)%>
<%: Html.TextBoxFor(x => x.Amount, new { disabled = Model.UseInvoicing ? "disabled" : String.Empty,
title = Model.UseInvoicing ? "the total payment amount of all selected invoices" : String.Empty,
#class = "small" })%>
<%: Html.ValidationMessageFor(x => x.Amount)%>
</p>
<p>
<%: Html.LabelFor(x => x.PayDate)%>
<%: Html.TextBox("PayDate", Model.PayDate.ToShortDateString(), new { #class = "medium" })%>
<%: Html.ValidationMessageFor(x => x.PayDate)%>
</p>
<script type="text/javascript">
$(function () {
quickPaySetup();
});
</script>
<div class="horizontalline"><%= Html.Spacer() %></div>
<%= FTNI.Controls.Submit("Submit Payment") %>
<%: Html.AntiForgeryToken() %>
<%: Html.ValidationMessage("Payment-Result")%>
<% } %>
And now my new payment method panel
<script type="text/javascript">
$(function () {
newPaymentSetup();
});
</script>
<h4>New Payment Method</h4>
<% if(Model.HasPaymentMethods) { %>
<span style="float:right;">
<%: Ajax.ActionLink("Cancel", "QuickPay",
new AjaxOptions() {
HttpMethod = "Get",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "quickpay-wrapper",
OnSuccess = "quickPaySetup",
LoadingElementId = "loading-pane"
})%>
</span>
<% } %>
<div>Enter the information below to create a new payment method.</div><br />
<%= Html.ClientValidationSummary(new { id = "valSumContainer" })%>
<% Html.EnableClientValidation(); %>
<div id="new-payment-method-tabs">
<ul>
<li>Credit Card</li>
<li>E-Check</li>
</ul>
<div id="new-credit-card">
<% Html.RenderPartial("NewCreditCard", Model.CreditCardModel); %>
</div>
<div id="new-ach">
<% Html.RenderPartial("NewACH", Model.ACHModel); %>
</div>
</div>
Each form starts off with something like this
<% using (Ajax.BeginForm("AddCreditCard", "Dashboard",
new { },
new AjaxOptions() {
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "quickpay-wrapper",
OnSuccess = "newPaymentSetup",
LoadingElementId = "loading-pane"
}, new { id="new-credit-card-form" })) { %>
<% ViewContext.FormContext.ValidationSummaryId = "valSumContainer"; %>
Initial load works. Any ajax replaces cause the form context to be lost and not reinitialize no matter what I do. The form posts back, validation occurs server side. All invalid fields are changed (css error classes added), but only the last error is shown in the summary.
I am going to tell you how I did just a few days ago. Please have a look to this question for details.
In my case I was showing the content of the form using an ajax call inside a jquery dialog. When the call complete I just replace the dialog content with the content sent back from the controller.
I have modified the code inside the Microsoft script as described in my question and then called the init method on document ready. This should work for your case too...
For the second error (how do I get all my errors to show in the validation summary?) I have simply modified the code as described in the same source post you are referring to and did not experienced any problem.
Hope it helps!
I ended up reworking my solution so that I was no longer writing the forms to the page through callbacks. While this was not my intended approach, it does work. I chose to use jquery modals to display the data rather than changing the content of one area of the screen.
Ideally, I would not have to render all of the forms to the page and could call them up on demand, but it seems jquery client side validation will not wire up unless the form is present on the page load. I am unsure of the form elements are required present on form load, but it may be a limitation I just have to deal with.
Another workaround would have been to render them all to the page and just show/hide each form through jquery. It's not much different than using modals, but at least the validation would work. I'd still like to see a solution where forms using validation summaries and client side jquery validation through data annotations can be written to the page through callbacks and still wired up and function correctly.