I have a model with data annotations and i am an dynamically binding that with viewmodel using knockout template binding and mapping plugin. I am trying to do a unobtrusive client validation to be done on my model. How we can do that in this scenario. Any help/suggestions?
public class MyUser
{
[Required]
[StringLength(35)]
public string Username { get; set; }
[Required]
[StringLength(35)]
public string Forename { get; set; }
[Required]
[StringLength(35)]
public string Surname { get; set; }
}
In my view i am dynamically template binding a list of MyUser using ajax.
public JsonResult TestKnockout()
{
IList<MyUser> myUserList = new List<MyUser>();
myUserList.Add(new MyUser { Username = "ajohn", Surname = "surname" });
myUserList.Add(new MyUser { Username = "ajohn1", Surname = "surname1" });
return Json(myUserList, JsonRequestBehavior.AllowGet);
}
}
View:
<form id="Userform" action='@Url.Action("Save", "Home")' data-bind="template: {name: 'UserTemplate', foreach:UserList}">
<input type="Submit" name="name" value="Submit" />
</form>
<script id="UserTemplate" type="text/Html">
<input type="text" data-bind="value: Username"></input>
<input type="text" data-bind="value: Forename"></input>
<input type="text" data-bind="value: Surname"></input>
</script>
<script type="text/javascript">
var viewModel = {
UserList: ko.observableArray(new Array()),
Save: function () {
//// reached here means validation is done.
alert("Save");
}
}
ko.applyBindings(viewModel);
$.ajax({
type: 'GET',
url: '../Home/TestKnockout',
contentType: "application/json",
success: function (data) {
$.each(ko.mapping.fromJS(data)(), function () {
viewModel.UserList.push(this);
})
// attach the jquery unobtrusive validator
$.validator.unobtrusive.parse("#Userform");
// bind the submit handler to unobtrusive validation.
$("#Userform").data("validator").settings.submitHandler = viewModel.Save;
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
</script>
pilavdzice and drogon answers are quite good but we forget the basic point.
Since we are using an MVVM pattern for the seperation of UI and data (+vm) we don't want to perform UI validation but DATA VALIDATION. Those two are quite different, jquery validate is a great plugin but it does UI validation (it starts from UI to check the fields).
I have found knockout validation plugin which seems to do quite well and what it does is to go the opposite road, it validates your viewmodel and not your UI (it actually maps to UI elements to display the errors).
Unfortunately If your viewmodel gets complex, that plugin will have some problems, but in any case this is the way to go.
UI validation is perfect as long as we are not using an MVVM pattern, after all what do we separate the components (M-V-VM) for ?
Hope I helped !
Thanks!
I had the same problem as you so I wrote the following component.
https://www.nuget.org/packages/ScriptAnnotations/
https://scriptannotations.codeplex.com/
Please let me know if this helps.
I would go with jquery's event binding for this.
First, add your data-val attributes to the inputs you want to validate. (To figure out which data-val attributes to use, I usually bind a form server-side to a model and view source.)
<input data-val-required="test" data-val="true" data-bind="visible:
$parent.userEditMode, value: FirstName" />
Second, add a validation utility function --this calls the jquery validation plugin used by MVC under the covers.
function validateForm(thisForm) {
var val = thisForm.validate();
var isValid = val.form();
alert(isValid);
if (!isValid) {
thisForm.find('.input-validation-error').first().focus();
}
return isValid;
}
Third, call validate before issuing your viewmodel method. Make sure to remove the "click" data-bind attribute from the markup in your page.
$('#..your form id...').live('submit', function (e) {
e.preventDefault();
if(validateForm($(this)))
viewModel.saveUser();
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With