3
votes

Consider the following ViewModel that is generated through the knockout mapping plugin.

var originalData = {

"QuoteSelectedViewModel": {
  "ProductName": "Select",      
  "CoverQuotesViewModel": [
     {
        "Code": 1,
        "Label": "Première Assistance 24h/24 (GRATUITE)",
        "IsMandatory": true,
        "IsSelected": true,            
        "DependsOn": []
     },
     {
        "Code": 2,
        "Label": "Assistance PLUS 24h/24",
        "IsMandatory": false,
        "IsSelected": false,          
        "DependsOn": []
     },
      {
        "Code": 8,
        "Label": "Heurts Animaux / Force de la Nature",
        "IsMandatory": false,
        "IsSelected": false,        
        "DependsOn": [
           2
        ]
     },
  ]}
}

var viewModel = ko.mapping.fromJS(originalData);

ko.applyBindings(viewModel);


<div data-bind="with: QuoteSelectedViewModel">
selected quote is : <span data-bind="text: ProductName"></span>
 <!-- ko foreach: CoverQuotesViewModel -->
<br/>    
  <div data-bind: if: IsVisible>
    <input type="checkbox" data-bind="checked: IsSelected"></input>
    <input type="text" data-bind="value: Label, enable: IsSelected"></input>
  </div>
<!-- /ko -->
</div>

Now, I would like to hide the div when IsVisible returns false. IsVisible does not exist yet, and it should be a computed observable function on each element of the CoverQuotesViewModel array.

How do I generate this computed observable function on each element ?

Thanks

[EDIT] I've added a jsfiddle here : http://jsfiddle.net/graphicsxp/fpKWM/

[EDIT2] Actually knockout document is clear about how to do that:

Of course, inside the create callback you can do another call to ko.mapping.fromJS if you wish. A typical use-case might be if you want to augment the original JavaScript object with some additional computed observables:

var myChildModel = function(data) { ko.mapping.fromJS(data, {}, this);

this.nameLength = ko.computed(function() {
    return this.name().length;
}, this); }

[EDIT]

Here's the full code following Paul's suggestion: (getQuotesSuccess is an AJAX success handler)

viewModel.getQuotesSuccess = function (result) {
var myCoverQuotesViewModel = function (data, parent) {
    var self = this;
    ko.mapping.fromJS(data, {}, this);

    self.IsVisible = ko.computed(function () {
        var visible = true;
        if (self.DependsOn().length > 0) {
            $.each(self.DependsOn(), function (index, value) {
                var dependency = viewModel.QuoteSelectedViewModel().CoverQuotesViewModel.filterByProperty("Code", value);
                if (dependency().length > 0) {
                    visible = visible & dependency()[0].IsSelected();
                } else {
                    visible = false;
                }
            });
        }

        return visible;

    }, this);
}

var mapping = {
    'CoverQuotesViewModel': {
        create: function (options) {
            return new myCoverQuotesViewModel(options.data, options.parent);
        }
    }
}

  ko.mapping.fromJS(result, mapping, viewModel);
};
1
Exactly what logic do you want IsVisible to work off?Paul Manzotti
let's assume the logic of IsVisible is irrelevant to the problem. It should return true or false. The issue is that I don't know how to generate dynamically this computed observable function on each element of the array.Sam
See my answer for how you can loop over each of the objects in your array. Is that not what you're after?Paul Manzotti

1 Answers

3
votes

Ok, reverting back to my earlier answer, with your modifications, so anyone else looking at this answer actually gets the correct version!

You need to create a child viwe model, and use the mapping plugin to populate it automatically, then add in your computed observable:

function CoverQuotesViewModel(data)
{
    var self = this;
    ko.mapping.fromJS(data, {}, self);

    // Copy the data to each property.
    self.IsVisible = ko.computed(function()
    {
            // your logic for each quote
    });
}

Then you need to use a create map for the mapping of the main view model, and in this you create your child view model:

var mapping = {
    'CoverQuotesViewModel': {
        create: function(options) {
            var model = new CoverQuotesViewModel(options.data);
            return model;
        }
    }
}
var viewModel = ko.mapping.fromJS(data, mapping);

You don't need to pass this into the computed, as you are referencing self, which is your stored version of this.