unobtrusive client validation in knockout template binding

HashCoder picture HashCoder · Feb 5, 2012 · Viewed 8.8k times · Source

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>

Answer

George Mavritsakis picture George Mavritsakis · Apr 21, 2012

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!