I'm gonna try to keep the code to a minimum here.
In my MVC3 application using c# I have a form in a partial view and the submit button in the main view. I have the [required] attribute on the model properties used in the partial view and also rules set out in an associated javascript file.
But when a user a doesn't enter any data on the field in the form no validation is executed and the application continues.
I assumed the following steps would be correct.
I have the Javascript file referenced in the main view.
The form Id is referenced in this javascript file as is the input id of the submit input form the main view.
Example of model property:
[Required]
[ScaffoldColumn(true)]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[DataType(DataType.Date)]
[Display(Name = "Expected Return Date to Customer")]
public DateTime? ExpectedReturnedToCustomer { get; set; }
Form tag in partial view:
<form id="multipleenroutetoltslab" method="post"
Partial view is called via #{Html.RenderPartial();}
Input tag used for Submit.
<input type="submit" style="width:100%;" class="styledbutton" id="MultiSubBtn" value="Submit" />
Javascript file reference in main view (part of a bundle):
.Add("~/Scripts/CalibrationViewer/MultipleEnRouteToLtsLab.js")
Javascript file :
$(document).ready(function () {
$('#MultiSubBtn').click(function () {
$('#multipleenroutetoltslab').validate({
errorClass: 'field-validation-error',
errorPlacement: function (error, element) { //place the error method after the date picker component
var trigger = element.next('.ui-datepicker-trigger');
error.insertAfter(trigger.length > 0 ? trigger : element);
},
rules: {
SentBy: {
required: true,
maxlength: 255
},
DateSentToLtsLab: {
required: true,
dateFormat: true
},
ExpectedReturnedToCustomer: {
required: true,
dateFormat: true
}
},
The view Partial View:
#model EnRouteToLts_RecByLts
<form id="multipleenroutetoltslab" method="post">
<p>
#Html.LabelFor(m => m.SentBy)
#Html.TextBoxFor(M => M.SentBy, new { disabled = "disabled", #readonly = "readonly" })
</p>
<p>
#Html.LabelFor(m => m.DateSentToLtsLab)
#Html.TextBox("DateSentToLtsLab", Model.DateSentToLtsLab.HasValue ? Model.DateSentToLtsLab.Value.ToShortDateString() : DateTime.Now.ToShortDateString(), new { disabled = "disabled", #readonly = "readonly" })
</p>
<p>
#Html.LabelFor(m => m.ExpectedReturnedToCustomer)
#Html.TextBox("ExpectedReturnedToCustomer", Model.ExpectedReturnedToCustomer.HasValue ? Model.ExpectedReturnedToCustomer.Value.ToShortDateString() : null, new { #class = "date-picker" })
</p>
</form>
There is more code the javascript file if needed.
I placed an alert after the $('#MultiSubBtn').click(function () { and it was fired so the file is being reached but rules are not being applied.
Also it should be noted when data is entered in the form it does reach the Post action.
No errors.
Related
I want to validate the entered username and password entered in the textboxes present in a form without Postback/Refresh. I know I am gonna need Javascript or AJAX for this purpose, but somebody please guide me through this like refer me to any tutorial or please explain me the code here.
My present code without this feature looks like this:
#using(Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.TextBoxFor(u => u.PPNumber, new { #class = "form-control", type = "number", placeholder = "Enter Number",#min="1" })
#Html.ValidationMessageFor(u => u.PPNumber)
#Html.TextBoxFor(u => u.Password, new { #class = "form-control", type = "Password", placeholder = "Password" })
#Html.ValidationMessageFor(u => u.Password)
<input type="submit" value="Login" class="btn btn-primary btn-block" />
}
You can use ajax.
When user submits the form, you need to hijack that event in javascript and stop that(prevent the normal full page form submit) and instead make an ajax call to the action method with the form data. This action method should have code to verify the user credentials and can return a JSON data structure which has a status property which says whether the credential validation is successful or not. You can inspect this response in your ajax call's success/done callback and act accordingly.
Here is a quick sample using jQuery $.post method.
Give an Id to your form so that we can use that to wire up the submit event.
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id="loginForm"))
{
#Html.TextBoxFor(u => u.PPNumber)
#Html.TextBoxFor(u => u.Password, new { #class = "form-control", type = "Password"})
<input type="submit" value="Login" class="btn btn-primary btn-block" />
}
and the javascript code to hijack the submit event and do an ajax post instead.
$(function () {
$("#loginForm").submit(function(e) {
e.preventDefault();
$.post($(this).attr("action"), $(this).serialize())
.done(function(response) {
if (response.status === 'success') {
alert("Login successful.Do something else now");
} else {
alert("Login failed");
}
});
});
});
Assuming your Login action method in AccountController will return a Json response with a status property.
public ActionResult Login(string PPNumber,string password)
{
if(PPNumber=="thisIsDemo" && password=="ButDoTheActualCheck")
{
return Json(new { status = "success" });
}
return Json(new { status = "failed" });
}
Here i just hard coded the usrename /password check to 2 static values. You can change that to check it against your db table/whatever your credentials checking mechanism is.
let's suppose I have a form like this:
#using (Ajax.BeginForm("ChangePassword", "User", new { area = "UserSettings" }, new
AjaxOptions
{
HttpMethod = "POST",
OnSuccess = "ShowMessage('Password successfully changed')"
}, null))
{
#Html.TextBoxFor(m => m.Password, new { placeholder = "Password", #class = "form-control", #type = "password" })<br />
#Html.ValidationMessageFor(model => model.Password, "", new { #class = "text-danger", #style = "float:right;" })
#Html.TextBoxFor(m => m.PasswordConfirm, new { placeholder = "Confirm password", #class = "form-control", #type = "password" })<br />
#Html.ValidationMessageFor(model => model.PasswordConfirm, "", new { #class = "text-danger", #style = "float:right;" })
<button type="submit" class="btn btn-primary">Change Password</button>
}
Essentially it's a form to change a password with validation setup in place.
Now my Question here is, in the case where I have an action like this:
[HttpPost]
[ActionName("ChangePassword")]
public ActionResult ChangePassword(MyObjectClass model)
{
if(!ModelState.IsValid)
{
return View(model);
}
else{
ViewBag.MyData = // some data here;
return View();
}
}
The questions that I have are:
When I return a View with all data in it, how do I actually then inject that returned data into the user's DOM that the user can see the changes reflected when OnSuccess method is called? Where is the "data" object that I can inject into like in done function in jQuery( .done(function(data)))?
What is the easiest way here to transfer now all the data from my controller action into the OnSuccess method and actually let the user see the outcome of the method that they called?
Can someone help me out with this ? What is the best way to solve this issue ?
I used to work with pure jQuery before and I Was doing this in this manner:
$.post("/linktomymethod",{/*some parameters*/).done(function(data){
// then inject the returned data into the browser's DOM
}));
If you want to use #Ajax.BeginForm(), then you specify the UpdateTargetId property of AjaxOptions, for example
<div id="changepassword">
#using (Ajax.BeginForm(..., new AjaxOptions { UpdateTargetId = "changepassword" }, null))
{
....
}
<div>
which would replace your <form> element with the view returned by your ChangePassword() method. Note also that your should be returning a PartialView.
However, you should stick with the $.ajax() methods as these give you far more flexibility. Typically you handle the forms .submit() event, check the .valid() method (and cancel the ajax call if not so client side validation messages are displayed), and then update the DOM with the partial view that the method returns.
Replace your #using (Ajax.BeginForm(..) { code with a simple
<div id="changepassword">
#using (Html.BeginForm()) {
....
and add the following script
var url = '#Url.Action("ChangePassword", "User", new { area = "UserSettings" })';
$('form').submit(function (ev) {
ev.preventDefault(); // prevent the default submit
if (!$(this).valid()) { // check if the data is valid
return; // exit the function
}
var formdata = $(this).serialize();`
$.post(url, formdata, function(response) {
$('changepassword').html(response);
});
});
To improve performance further, your could just return a JsonResult containing the invalid properties and their associated error, and update the placeholder elements generated by #Html.ValidationMessageFor(). You controller code would be
if (!ModelState.IsValid)
{
var errors = ModelState.Keys.Where(k => ModelState[k].Errors.Count > 0).Select(k => new { propertyName = k, errorMessage = ModelState[k].Errors[0].ErrorMessage });
return Json(errors);
}
// Save data
return Json(null);
In the success callback, you can then loop through the collection, find the corresponding ValidationMessageFor() placeholder based on the property name, and add the error message and appropriate class names.
I am attempting to using knockout to create a client side model so client side validation can be done on the needed attributes, some of which are nested and or in nested lists of other models.
In following some guides and patterns, I have tried to map a test list from the main view model and send it back to the controller when the form submits, with validation that would prevent the form from being submitted if the value is null.
When the form is submitted, not only does it fail to validate with the current set up, the edited values (which are correctly populated in the view on load, so some type of binding is correctly working) return as null in the controller.
namespace KnockoutDemo.Models
{
public class XmlParameter
{
public HttpPostedFileBase XmlValue;
public string Name;
}
}
public class TestStepViewModel
{
public int TestStepId { get; set; }
public string TestStepName { get; set; }
public string Message { get; set; }
public List<XmlParameter> XmlParameters { get; set; }
}
View
#using System.Web.Mvc.Ajax
#using System.Activities.Expressions
#using System.Web.Script.Serialization
#model KnockoutDemo.Models.TestStepViewModel
#{ string data = new JavaScriptSerializer().Serialize(Model);}
#section scripts
{
<script src="~/Scripts/knockout-3.4.0.js"></script>
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/teststepviewmodel.js"></script>
<script type="text/javascript">
var testStepViewModel = new TestStepViewModel(#Html.Raw(data));
ko.applyBindings(testStepViewModel);
</script>
}
<form>
<div>
<div class="form-group">
<label class="control-label" for="TestStepName">Test Step Name:</label>
<input class="form-control" name="TestStepName" id="TestStepName" data-bind="value: TestStepName"/>
</div>
<div class="form-group">
<label class="control-label" for="TestStepName">Test Step Id:</label>
<input class="form-control" name="TestStepId" id="TestStepId" data-bind="value: TestStepId" disabled="disabled"/>
</div>
<table class="table table-striped">
<tr>
<th>Product Code</th>
</tr>
<tbody data-bind="foreach: XmlParameters">
<tr>
<td class="form-group"><input name="Name" class="form-control input-sm" data-bind="attr: {'id': 'Name_' + $index()}, value: Name"/></td>
</tr>
</tbody>
</table>
</div>
<p><button class="btn btn-primary" data-bind="click: save" type="submit" >Save</button></p>
</form>
teststepviewmodel.js + validation
TestStepViewModel = function(data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.save = function () {
$.ajax({
url: "/Home/Save/",
type: "POST",
data: ko.toJSON(self),
contentType: "application/json"
});
}
}
var XmlParameter = function(data) {
var self = this;
ko.mapping.fromJS(data, mapping, self);
}
var mapping = {
'XmlParameters': {
key: function (xmlParameters) {
return ko.utils.unwrapObservable(xmlParameters.Name);
},
create: function (options) {
return new XmlParameter(options.data);
}
}
};
$("form").validate({
submithandler: function () {
testStepViewModel.save();
},
rules: {
TestStepName: {
required: true,
maxlength: 30
},
Value: {
required: true
},
XmlValue: {
required: true
}
},
messages: {
TestStepName: {
required: "A Test Step must have a non-null value, please enter a name"
},
Value: {
required: "The parameter can't be null/empty"
}
}
});
The JsonResult Save() controller correctly populates the Id and Test Step Name, however the XmlParameters are both null.
Controllers (Like I said, this is simply a test to return knockout model with client side validation, so I'm simply populating a view model on load and setting a breakpoint on the JsonResult to see the contents of the model)
public ActionResult Index(TestStepViewModel ts)
{
TestStepViewModel testStepViewModel = new TestStepViewModel
{
TestStepName = "Editing A Test Step",
TestStepId = 10,
Message = "Hello, this is a message"
};
testStepViewModel.XmlParameters = new List<XmlParameter>();
testStepViewModel.XmlParameters.Add(new XmlParameter
{
Name = "Xml P1"
});
testStepViewModel.XmlParameters.Add(new XmlParameter
{
Name = "Xml P2"
});
return View("Index", testStepViewModel);
}
public JsonResult Save(TestStepViewModel testStepViewModel)
{
return null;
}
I am trying to submit an ajax form that is loaded via a partial page. The submit comes from a button that is created on a JQuery UI Dialog. The form data is included in the request but when I step through the code on the server side the view model is not being populated with the data.
Javascript that loads the partial and after load is complete creates the dialog.
$('#test').load('#Url.Action("NewIntegrationLevel", "IntegrationLevelConfig", null, Request.Url.Scheme)',
function() {
var dialog = $('#addEditForm');
dialog.dialog({
resizable: false,
modal: true,
width: 500,
title: 'New Integration Level',
buttons: [{
text: 'Save',
click: function(e) {
e.preventDefault();
$('#addEditForm').submit();
dialog.dialog('close');
},
type: 'submit',
form: 'addEditForm'
},
{
text: 'Close',
click: function(e) {
e.preventDefault();
dialog.dialog('close');
}
}
]
});
});
My partial page:
#model Zodiac.Cmt.UI.Models.IntegrationLevelConfigViewModel
#using (Ajax.BeginForm("Save", "IntegrationLevelConfig", FormMethod.Post, null, new { id = "addEditForm" }))
{
<div id="addEdit-integration-dialog">
<div>
<div>
#Html.LabelFor(model => model.Name)
#Html.TextBoxFor(model => model.Name)
</div>
</div>
</div>
}
Server side code:
[HttpPost]
public ActionResult Save(IntegrationLevelConfigViewModel viewModel)
{
return PartialView("_AddEditIpl", viewModel);
}
Since the JQuery dialog creates the submit button outside the form I am using the "form" attribute on the button so that it knows which form to submit.
This is what the request looks like after submitting the page:
So, why doesn't the viewmodel get populated with the form data on the server side???
ViewModel:
public class IntegrationLevelConfigViewModel
{
[Description("Name")]
public string Name;
}
I figured it out.
In my view model
public string Name;
needs to change to:
public string Name {get;set;}
I am trying to submit an aria template form http://ariatemplates.com/,the submission is done to a Spring MVC controller/servlet.
The form is getting submitted all right but i am not able to get the values of the aria elements like date picker,text box etc in the controller.
Request.getParameter is of no use.
Any Help will be appreciated.
Here is my sample tpl file,js file and the Spring Controller.
TPL File
{Template {
$classpath:'view.Turnover',
$hasScript : true
}}
{macro main()}
<form action="test.do" method="POST" id="turnoverform">
<div style="float:left;padding-top: 3em;padding-bottom: 3em;padding-right: 3em;">
{#aria:Div {
sclass : "basic",
width : 740,
height : 300
}}
<p style="font-family:Arial,Helvetica,sans-serif;font-size: medium;">Create Turnover Report</p>
<hr />
{#aria:DatePicker {
label: " begin date:",
labelWidth:190,
width:330,
helptext:"Type date or select",
}/}
{#aria:DatePicker {
margins:"x x x 20",
label: "end date:",
labelWidth:190,
helptext:"Type date or select",
width:330,
}/}
<br/>
<br/>
<br/>
{#aria:TextField {
label : "User id",
labelPos : "left",
helptext : "ID",
width : 250,
block : true,
labelWidth : 80,
bind : {
"value" : {
inside : data,
to : 'value' }
}
}/}
<br />
{/#aria:Div}
<br />
{#aria:IconButton {
icon: "std:confirm",
label:"Create",
width : 300,
tooltip : "Click on this to create a Report",
block: true,
onclick : {
fn : buttonClick
}
} /}
</div>
</form>
{/macro}
{/Template}
Javascript File :
Aria.tplScriptDefinition({
$classpath : "view.TurnoverScript",
$prototype : {
/**
* Callback for the click event on the first button.
* #param {aria.DomEvent} evt Click event
*/
buttonClick : function (evt) {
aria.core.IO.asyncFormSubmit({
formId : "turnoverform",
callback : {
fn : this.onSuccess,
onerror : this.onError,
scope : this
}
});
},
onSuccess : function (evt, args) {
alert("The Template has been created");
//this.$json.setValue(["view:Dialog"], "dialogOpen", true);
},
onError : function (evt, args) {
alert("The Template has not been created due to some Error");
}
}
});
in Aria Templates you don't work normally with DOM elements but with the data model.
The way to achieve what you want is to bind those values to the datamodel using the bind property
{#aria:DatePicker {
label: " begin date:",
labelWidth:190,
width:330,
helptext:"Type date or select",
bind : {
value : {
inside : data,
to : "begin_date"
}
}
}/}
Your datamodel would now contain those values, try to modify those values and see the content of this.data in your template script.
To submit the data you have two options,
Template Script through aria.core.Io.asyncRequest (or maybe the RequestMgr, depending on your application complexity).
This method takes a data string that in case of POST requests is the message body. It has to be a string so you can use aria.utils.json.JsonSerializer.serialize() to convert your datamodel into a string.
aria.utils.json.JsonSerializer.serialize(this.data, config)
In the previous snippet of code config is optional, if provided it should match this bean.
Module controller through submitJsonRequest
The good thing about using a controller is that you separate the logic of connecting to a server from the template and that you can send directly an object as data, serialization is done internally.
The drawback is that you'll probably have to configure your UrlService to convert actions to actual URL. Few more info here