KnockoutJS - Adding computed values to an observable array
Asked Answered
F

3

6

I'm binding data to a page using KnockoutJS, the ViewModel is being populated by an JSON response from an AJAX call using the mapping plugin, like this:

$(function () {
    $.getJSON("@Url.Action("Get")", 
        function(allData) {
            viewModel = ko.mapping.fromJS(allData);

            viewModel.Brokers.Url = ko.computed(function()
            {
                return 'BASEURLHERE/' + this.BrokerNum();
            });

            ko.applyBindings(viewModel);
    });
});

The middle part there doesn't work (it works fine without that computed property). "Brokers" is an observable array, and I want to add a computed value to every element in the array called URL. I'm binding that Brokers array to a foreach, and I'd like to use that URL as the href attribute of an anchor. Any ideas?

Faze answered 22/2, 2012 at 21:29 Comment(0)
S
12

Well, if you want Url in each broker, you have to add it to each broker:

$.each(viewModel.Brokers(), function(index, broker){
    broker.Url = ko.computed(function(){return 'BASEURLHERE/' + broker.BrokerNum();});
});

I guess BrokerNum is not going to change, so you might as well just calculate Url once:

$.each(viewModel.Brokers(), function(index, broker){
    broker.Url = 'BASEURLHERE/' + broker.BrokerNum();
});

You can also add Url property during mapping by providing "create" callback to ko.mapping.fromJS function. See mapping plugin docs for details.

If you only need url to bind to href, just bind the expression in html (within foreach binding):

<a data-bind="attr: {href: 'BASEURLHERE/' + BrokerNum()}">link to broker details</a>
Sextodecimo answered 22/2, 2012 at 21:51 Comment(0)
K
13

I've been working through very similar issues and I've found that you can intercept the creation of the Broker objects and insert your own fields using the mapping options parameter:

var data = { "Brokers":[{"BrokerNum": "2"},{"BrokerNum": "10"}] };

var mappingOptions = {
    'Brokers': {
        create: function(options) {
            return (new (function() {
                this.Url = ko.computed(function() {
                    return 'http://BASEURLHERE/' + this.BrokerNum();
                }, this);

                ko.mapping.fromJS(options.data, {}, this); // continue the std mapping
            })());
        }
    }
};

viewModel = ko.mapping.fromJS(data, mappingOptions);

ko.applyBindings(viewModel);

Here is a fiddle to demonstrate this: http://jsfiddle.net/pwiles/ZP2pg/

Kwok answered 5/2, 2013 at 8:14 Comment(0)
S
12

Well, if you want Url in each broker, you have to add it to each broker:

$.each(viewModel.Brokers(), function(index, broker){
    broker.Url = ko.computed(function(){return 'BASEURLHERE/' + broker.BrokerNum();});
});

I guess BrokerNum is not going to change, so you might as well just calculate Url once:

$.each(viewModel.Brokers(), function(index, broker){
    broker.Url = 'BASEURLHERE/' + broker.BrokerNum();
});

You can also add Url property during mapping by providing "create" callback to ko.mapping.fromJS function. See mapping plugin docs for details.

If you only need url to bind to href, just bind the expression in html (within foreach binding):

<a data-bind="attr: {href: 'BASEURLHERE/' + BrokerNum()}">link to broker details</a>
Sextodecimo answered 22/2, 2012 at 21:51 Comment(0)
M
0

Thanks to Peter Wiles i have very similar solution:

var ViewModel = function (data, ranges) {
    var self = this;

    this.productList = ko.observableArray();
    var productListMapping = {
        create: function (options) {
            return (new (function () {
            //this row above i don't understand...
                this.len = ko.computed(function () {
                    //just test function returning lenght of object name
                    // and one property of this model
                    return this.name().length + ' ' + self.cons_slider_1();
                }, this);

                ko.mapping.fromJS(options.data, {}, this); // continue the std mapping
            })());
        }
    }

    this.cons_slider_1 = ko.observable(100);

    ko.mapping.fromJS(data, productListMapping, this.productList);
};

Some differences: I am not mapping to self, but on this.product. The input json has not parent name like 'Brokers' in above example:

var products = [
    { "id": "pp1", "name": "Blue windy" },
    { "id": "pp1", "name": "Blue windy" }];

So in productMapping i'm typing just 'create:'

But, what i do not understand is the structure of create function. Could somebody explain me why the function returns new function, which has property. Couldn't it be simplified somehow?

Micrometeorology answered 30/1, 2014 at 10:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.