3
votes

I develop a project with Durandal/Breeze using intensive Knockout bindings.

I have a view which is using the following observable:

packing
  - description
  - weight
  - ...
  - isotopes
    - name
    - activity
    - ...

As you can see above: my packing observable contains an isotopes observableArray inside. This packing object is filled from breeze with a query.

var query = entityQuery.from('Packings')
        .where('id', '==', packingId)
        .expand('isotopes');

I try to set validation in place for all of this using ko.validation.

  • The description property of my packing observable is required

  • The name property of my isotopes observableArray is required

I successfully validate on description. So whenever user clear the input field binded to description, this one is highlighted in red. And whenever user add a new empty entity (breeze) and click on save, this one is highlighted in red.

This works thanks to this code in the save button:

var validationErrorsCount = ko.computed(function () {
    if (typeof packing() == 'undefined') return;
    var validationErrors = ko.validation.group(packing());
    return validationErrors().length;
})

if (validationErrorsCount() > 0) {
    logError('Validation failed, please check.', '', true);
    ko.validation.group(packing()).showAllMessages();
    return;
}

So far so good.

Now I need to validate the property name for my isotopes observableArray. So whenever user clear the input field binded to name, this one is highlighted in red. It works. But the problem is whenever user add a new empty entity (breeze) of type isotope, don't type anything for the name input box and click on save, this one is not highlighted in red.

When I debug and inspect values I can clearly see that:

  • ko.validation.group(packing(), {deep:false}); did not return any invalid things

  • ko.validation.group(packing().isotopes(), {deep:false}); did not return any invalid things

So it seems that ko.validation did not detect my invalid inputs.

My question: how to validate my nested isotopes observableArray when I add a new element inside?

Thanks.


UPDATE

Here is another SO post with the problem: Breeze.js & Knockout.js: translating breeze validation to knockout validation causes an 'Out of stack space' or 'Too much recursion'

Here is my 'dirty' & temporary hack (from line 4 to 9)

// Check errors on the packing entity
var validationErrorsCount = ko.validation.group(packing()).length;
// Check errors on the isotopes entities !! code below is a temporary hack 
ko.utils.arrayForEach(packing().isotopes(), function (isotope) {
    if (!isotope.name.isValid()) {
        validationErrorsCount = validationErrorsCount + 1;
        ko.validation.group(isotope).showAllMessages();
    }
});

if (validationErrorsCount > 0) {
    logError('Validation failed, please check.', '', true);
    ko.validation.group(packing()).showAllMessages();
    return;
}

Still waiting for a better way for validating my inner (nested) models.

2

2 Answers

1
votes

I do not think there is an easy way. At a certain degree of complexity, it's easier to move to an "ItemViewModel". An ItemViewModel is a view-oriented wrapper around an inner entity. It exposes carefully controlled properties designed for easier binding. I described the approach here in connection with a different problem.

I don't know if you've hit that wall.

Another thought: write a couple of custom validations for this EntityType that do the hard work of inspecting the isotopes. You'll know how to do the job "right" and efficiently in your validation method; you won't be relying on KO to walk the graph or suffer its problems with circularities.

1
votes

What if you change: ko.validation.group(packing().isotopes(), { deep:false });

to: ko.validation.group(packing().isotopes(), { deep:true });

Isn't that the point of deep?