unobtrusive client validation in knockout template binding
C

4

10

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>
Cupule answered 5/2, 2012 at 7:47 Comment(0)
B
11

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!

Brookebrooker answered 21/4, 2012 at 9:57 Comment(4)
hi i'm just exploring knockoutvalidation plugin and I just want to know whether it supports server side data annotations out of the box?? or do one need to implement all of the validation on the clientside viewmodel. thanks?Kent
As a side note - I think that first part @pilavdzice comment was pretty close to that - it relies on a method from the ViewModel to provide the results of data validation.Lachance
Yes, it is closer to model validation, but on the other side, that solution relies on the UI calling specific function to validate an element of the model. This means model and ui are coupled. The best solution would be for the model to auto validate and the ui just handle/display validation results.Brookebrooker
Also, another project regarding knockout-validation is knockoutvalidator.codeplex.com which is my - premature - atempt to handle model validations including array elements.Brookebrooker
T
4

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.

Touter answered 19/4, 2014 at 2:49 Comment(1)
This seems really interesting. I will have a lookChilt
A
2

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();
});
Ashaashamed answered 7/2, 2012 at 15:49 Comment(0)
D
2

If you are using knockoutjs and jquery, I came up with the following very simple method for doing basic validation.

Wherever you want to display the error message on your page, include a span tag like this:

<span name="validationError" style="color:Red" 
data-bind="visible: yourValidationFunction(FieldNameToValidate())">
* Required.
</span>

Obviously you need to write "yourValidationFunction" to do whatever you want it to do. It just needs to return true or false, true means display the error.

You can use jquery to prevent a user from proceeding if any validations errors are displayed. You probably already have a save button that triggers a javascript function to do some ajax or whatever, so just include this at the top of it:

 if ($("[name='validationError']:visible").length > 0) {
        alert('Please correct all errors before continuing.');
        return;
    }

This is a lot simpler and more flexible than many other validation solutions out there. You can position your error message wherever you want, and you don't need to learn how to use some validation library.

Diffident answered 9/3, 2012 at 20:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.