1
votes

I'm building a mechanism to automatically display validation messages in a form field and would like to discuss about this.

I have an observable array with the error message and its respective properties.

How can I bind an element of the observable array to its respective form field?

The observable has the following data (i.e):

messages = ko.observableArray();
- Property: Reference, Message: Already exists a product with this reference.
- Property: Name, Message: The product's name is mandatory.
- Property: Bar Code, Message: The Bars Code does not matches the EAN13 format.
- ...

My form fields' IDs have the same names of the properties in the array:

        <div class="s13">
            <label for="Reference">Reference</label>
            <input class="text-box single-line" id="Reference" name="Reference" type="text" value="" data-bind="value: selected().Reference" />
            <span id="vReference"></span>
        </div>

        <div class="s13">
            <label for="Name">Name</label>
            <input class="text-box single-line" id="Name" name="Name" type="text" value="" data-bind="value: selected().Name" />
            <span id="vName"></span>
        </div>

        <div class="s13">
            <label for="BarCode">Bars Code (EAN13)</label>
            <input class="text-box single-line" id="BarCode" name="BarCode" type="text" value="" data-bind="value: selected().BarCode" />
            <span id="vBarCode"></span>
        </div>

I'm very new to Knockout.js but I think that I'll need to use a ´computed observable´, which returns the respective message for each span.

What you say?

/// UPDATE ////////////////////////////////////////////////////

The Products View Model:

function mainmodel(baseUri) {

var m = this;
    m.baseUri = baseUri;

/// PRODUCTS VIEW MODEL ******************************************* *

    m.products = new function () {

        var p = this;
            p.baseUri = baseUri;
            p.items = ko.observableArray();
            p.selected = ko.observable();
            p.messages = ko.observableArray([]);

The Products Loading

        /// LIST
            p.list = function () {
                $.getJSON(p.baseUri + "/list" + "?page=" + p.currentpage(), p.items);
            }

The Product Creation (where the validation messages are loaded)

        /// CREATE
            p.create = function (formElement) {
                //$.post(p.baseUri + "/create", $(formElement).serialize(), null, "json")
                $.ajax({
                    type: 'POST',
                    url: p.baseUri + "/create",
                    data: $(formElement).serialize(),
                    success: null,
                    dataType: "json",
                    statusCode: {
                        400: function (o) {
                            p.messages($.parseJSON(o.responseText));
                        }
                    }
                });
            }

Running this, p.messages contains validation messages for each of the product's properties.

1
Are these validation messages coming back from the server via an AJAX request?RP Niemeyer
Yes, when the client calls, for example, a Create method in my web api, it returns an HTTP Response and an object with all the validation messages. Then I process this messages filling the observableArray.TPaim
Isn't this a job for jquery? Something like: ko.utils.arrayForEach(messages, function (message) { $("#v" + message.Property).html(message.Message); }TPaim
Doing it this way goes outside of the normal flow when using Knockout. You really want to have your view model represent a state that your view binds against. The more code that you have outside of view-viewmodel, the harder it becomes to maintain. So, you could choose to do it this way, but it is not the "normal" way to do it with Knockout.RP Niemeyer
I see, you're in the right. I'll keep this way to finish this sprint, but I'll change soon.TPaim

1 Answers

0
votes

I think that there are a couple of approaches that you could use for this one. If you are receiving these validation messages back from a request to the server, then you could loop through them and populate observables directly based on the results.

A nice way to do this is to create a "sub" observable to hold the validation message like:

this.myObservable = ko.observable();
this.myObservable.validationMessage = ko.observable();

Now you can bind against myObservable and myObservable.validationMessage.

So, if you received an array of messages back you could loop through them and apply the messages like:

        var messages = [
            { Property: "Reference", Message: "Can't find a reference to this reference" },
            { Property: "Name", Message: "Pick a better name" },
            { Property: "BarCode", Message: "Not a valid code" }
        ];

        //apply messages to each observable
        ko.utils.arrayForEach(messages, function(message) {
            if (ko.isObservable(self[message.Property])) {
               self[message.Property].error(message.Message);  
            }
        });

I don't know your application flow, but you might want to clear the errors first.

Here is a sample: http://jsfiddle.net/rniemeyer/mk2Ed/

Here is an alternate that uses computed observables (the first would be slightly more efficient): http://jsfiddle.net/rniemeyer/R6mL8/