paulpitchford
paulpitchford

Reputation: 560

Referencing a computed property from a child in Knockoutjs

So I have a model:

// Parent View Model
GameViewModel = function (data) {
    var self = this;
    ko.mapping.fromJS(data, resultItemMapping, self);

    self.PlayerCount = ko.computed(function () {
        return self.Results().length;
    });
};

And using these mappings it's children:

var resultItemMapping = {
    'Results': {
        key: function (resultItem) {
            return ko.utils.unwrapObservable(resultItem.Id);
        },
        create: function (options) {
            return new ResultItemViewModel(options.data); 
        }
    }
};

// Child View Model
ResultItemViewModel = function (data) {
    var self = this;
    ko.mapping.fromJS(data, resultItemMapping, self);

    self.Points = ko.computed(function () {
        return (parent.PlayerCount);
    });
};

You can see that the Game View Model (parent) has a computed observable called PlayerCount (self.PlayerCount) and I can successfully bind this to my view and the amount increases / decreases accordinfly depending on the length of the Results (child) collection.

The problem lies with with the child computed observable called Points (self.Points). The controls when bound to it are always empty, and if I try and do something like return (parent.PlayerCount * 10) I always get NaN returned.

Ideally based on the player count, the result item points total should be n * PlayerCount.

Clearly what I've coded does not work correctly, but I do not get any console error messages. Is there a better method of doing what I'm trying to achieve?

Upvotes: 0

Views: 984

Answers (1)

Tomalak
Tomalak

Reputation: 338336

That's easy. The mapping plugin provides the parent for you:

var GameViewModel = function (data) {
    var self = this;

    self.Results = ko.observableArray();    
    self.PlayerCount = ko.pureComputed(function () {
        return self.Results().length;
    });

    ko.mapping.fromJS(data, GameViewModel.mapping, self);    
};
GameViewModel.mapping = {
    Results: {
        key: function (resultItem) {
            return ko.unwrap(resultItem.Id);
        },
        create: function (options) {
            return new ResultItemViewModel(options.data, options.parent); // <- here
        }
    }
};

var ResultItemViewModel = function (data, parent) {
    var self = this;

    self.Id = ko.observable();
    self.Points = ko.pureComputed(function () {
        return parent.PlayerCount();
    });

    ko.mapping.fromJS(data, ResultItemViewModel.mapping, self);
};
ResultItemViewModel.mapping = {
    // I can't imagine you want the same mapping definition as in the GameViewModel
};

Note that I've made a few other subtle changes, most prominently using pure computeds. I also like to make the viewmodel properties explicit instead of depending entirely on the mapping plugin. This and prevents code from accessing properties that don't exist and generally makes debugging a lot easier.

Upvotes: 1

Related Questions