Jason Richmeier
Jason Richmeier

Reputation: 1645

Knockout / Complex Object Issue

Let me preface this question by stating that I have only started working with the Knockout library yesterday afternoon so this quite possibly could be a factor in this issue.

Before I get into the code, I am trying to create a model that a Competition object which has a Name property and a Criteria property which is a collection of Criterion objects. A Criterion object has a PrimaryDescription and SecondaryDescription property.

Here is what I have so far:

function Competition(name, criteria)
{
  var self = null;

  self = this;

  self.Name = ko.observable(name);
  self.Criteria = criteria;

  return;
}

function Criterion(id, primaryDescription, secondaryDescription)
{
  var self = null;

  self = this;

  self.Id = id;
  self.PrimaryDescription = ko.observable(primaryDescription);
  self.SecondaryDescription = ko.observable(secondaryDescription);

  return;
}

In the view model, I have a Competition property that is set up like this:

self.Competition = ko.observable(new Competition('Competition 1', ko.observableArray([new Criterion(-1, 'Criterion 1', 'Criterion 1'), new Criterion(-1, 'Criterion 2', 'Criterion 2')])));

When the page loads, a binding to Competition().Criteria() works as expected.

In the view model, I have added the following:

self.addCriterion =
  function ()
  {
    self.Competition().Criteria().push(new Criterion(-1, '???', '???'));

    alert(self.Competition().Criteria().length);

    return;
  }

When I call this method, the new element gets added to the array and the array length is accurately reflected. However, the binding is not updated. I messed around a bit and changed the line in the Competition model part from "self.Criteria = criteria" to "self.Criteria = ko.observableArray(criteria)". When I do this and call the addCriterion method, the new element is added to the array and the binding updates the UI but the length of the array is reported as zero.

Since I can only get this working halfway, this tells me I am doing something wrong. What am I missing?

Many thanks in advance to whoever can steer me in the right direction.

Upvotes: 1

Views: 193

Answers (2)

Chris Pratt
Chris Pratt

Reputation: 239440

First off, not everything needs to be an observable. In fact, things should only be an observable if you truly need that functionality. There's a ton of overhead involved in constructing, initializing, and updating observables.

You haven't shown your view model (the thing you register with ko.applyBindings), but typically, your view model will just be the thing you're interacting with, not some abstraction over that. In other words, if your view is centered around the concept of a "Competition" then Competition, itself, would be your view model for the page.

Nevertheless, don't create an observable of an object. Knockout can't detect changes within an object, only changes to the whole object, i.e. you store a completely new instance of Competition in the self.Competition observable. As a result, just treat it as a simple JS var on your view model:

self.Competition = new Competition( ... );

Then if you want to reference an observable inside that, you just chain it like normal in JS: self.Competition.SomeObservable().

@jaux is right about the reason for your specific issue.

Upvotes: 2

Ye Liu
Ye Liu

Reputation: 8986

You should call self.Competition().Criteria.push instead of self.Competition().Criteria().push, because self.Competition().Criteria() will get you the original ordinary array.

Upvotes: 2

Related Questions