0

I'm using the ko mapping plugin to create my observables from a JS object, ko.mapping.fromJS().

A snippet of the object is below:

{ Name: "blah", Parent: { Title: "blah", Url: "/blah" } }

If Parent.Title changes then everything on my page is updating as expected, but when Parent becomes null I have a problem. The simplified markup that uses the Parent properties looks like this:

<p data-bind="if: HasParent">Up: <a data-bind="text: ParentTitle"></a></p>

HasParent looks like this:

self.HasParent = ko.computed(function () {
    return self.Parent;
});

ParentTitle looks like this:

self.ParentTitle = ko.computed(function () {
    return self.HasParent() ? self.Parent.Title() : "";
});

Note: self is set to the result back from ko.mapping.fromJS() and then applied to the page by calling ko.applyBindings();

So basically my problem is the HasParent function is always returning a true-ish value.

Also, this is my first ko project so if I can do anything in a better way please let me know :)

Any help would be appreciated.

Thanks.

Colin
  • 2,442
  • 5
  • 24
  • 30

2 Answers2

2

observables are functions, so even if their actual value is null, it will still be truthy when looking at the observable itself.

The if binding will unwrap your computed observable, but yours will have two levels that would need to be unwrapped to get to the actual value (the computed and then the actual observable).

You would want to return self.Parent() to have it work in the way that you intend. If you truly want a true/false value (not required for if) then you can do !!self.Parent().

Update:

The issue is that the mapping plugin only creates observables on the outer-most properties, so Title and Url will be observables, but not Parent. If you want your computed observable to actually respond to the Parent moving between null and populated, then it would need to be observable.

RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211
  • Thanks for your help. But if I return self.Parent() I get an error saying that self.Parent is not a function in the case where self.Parent is not null. Am I not binding the property correctly? – Colin May 02 '12 at 20:31
  • Maybe we can get a jsFiddle going for this one? Are you ever setting parent directly (`self.Parent = someObject` rather than `self.Parent(someObject)`). It might help to see how you are updating the object and making the calls to the mapping plugin. – RP Niemeyer May 02 '12 at 21:06
  • The issue is that the mapping plugin only creates observables on the outer-most properties, so `Title` and `Url` will be observables, but not `Parent`. If you want your computed observable to actually respond to the Parent moving between null and populated, then it would need to be observable. – RP Niemeyer May 02 '12 at 22:03
  • Yep that's right, Title and Url are observables. From what I can tell I need to make Parent an observable like you and @Michael Best are saying? And then in the markup use Parent().Title? I'm not directly setting any property as such, I'm letting the mapping plugin handle it as I'm passing JS objects about e.g ko.mapping.fromJS(data, model); – Colin May 03 '12 at 00:31
  • Yes, that is correct. One option is to use the [create](http://knockoutjs.com/documentation/plugins-mapping.html#customizing_object_construction_using_create) callback to make Parent an observable. – RP Niemeyer May 03 '12 at 01:28
  • Here is a sample: http://jsfiddle.net/rniemeyer/uFqFh/. The `HasParent` computed observable is a little bit redundant, since it just returns `Parent`. You could just use `Parent` directly. – RP Niemeyer May 03 '12 at 01:39
  • Ah brilliant, thanks for all the help! Logically, I would need to do something similar when updating right? Because everything works when Parent has the Title property set initially, but when its null initially and then Title is assigned a value HasParent is still false. – Colin May 03 '12 at 02:25
  • How are you updating it? Maybe you can demonstrate based on that fiddle. Are you saying that Parent is an object, but has a null Title? In that case `HasParent` should be true. – RP Niemeyer May 03 '12 at 02:48
  • I've created a new fiddle with basically how I'm doing everything. http://jsfiddle.net/uFqFh/1/ If you hit update I try and update Parent (which is null initially) to have properties. – Colin May 03 '12 at 03:34
  • Here is an update: http://jsfiddle.net/rniemeyer/uFqFh/2/. When you just have `Parent` with no sub-properties, then the mapping plugin does make it an observable. You can do a quick check in your mapping options code to handle it either way. – RP Niemeyer May 03 '12 at 04:14
  • Ah gotcha, see what you've done there. Thanks heaps for your help! Do I need to do this sort of set up for all deeply nested objects that can be null? I need to come up with a better way of initialising nullable objects as a blanket rule... – Colin May 03 '12 at 04:45
0

Parent isn't an observable; it's an object. HasParent won't ever get re-evaluated because it's not dependent on any observable. Changing your binding from if: HasParent to if: Parent should help you out.

Michael Best
  • 16,623
  • 1
  • 37
  • 70