Does Knockout support nested observables and binding paths?

7,931 views
Skip to first unread message

Jon Rimmer

unread,
Jun 27, 2011, 6:30:36 AM6/27/11
to knock...@googlegroups.com
Hi,

I'm currently looking at using KnockoutJS in a new project, and had some questions about it's capabilities. My previous experience with MVVM and data-binding was with Silverlight, and in Silverlight you could easily have an observable property on the view model that itself was an object expose observable properties, and so on.

For example, you might have an object tree that looks like this...
  • ViewModel
    • CurrentPerson
      • Name
      • Birthday
      • Address
        • HouseName
        • HouseNumber
        • Street
        • PostCode
...and each property would be observable. You could then create a binding with a path syntax like this 'ViewModel.CurrentPerson.Name.Address.HouseName' and it would update not only when the HouseName property on the address changed, but also when any of the other property properties in the path changed. That way, you could bind the value of the CurrentPerson property to, for example, the selected value of some combo box, and when the select CurrentPerson changed, all the properties with binding paths that referenced it would update, keeping the whole UI in sync.

From the documentation, I can't see any way to achieve this, as all the examples seem to be based around binding everything to single properties on the view model, but perhaps there's something I'm missing?

Thanks,

Jon

Andrew Booth

unread,
Jun 27, 2011, 7:22:48 AM6/27/11
to knock...@googlegroups.com
Hi Jon

Yes, Knockout bindings allow selecting a nested property, and complex dependency chains can be set up. This quick sample http://jsfiddle.net/andybooth/pQFRd/ has a nested address object and select list (combo box) to choose an item. Changes to the item values update the combo box labels.

Andy

Mark Bradley

unread,
Jun 27, 2011, 7:32:39 AM6/27/11
to knock...@googlegroups.com

Out of curiosity: in your scenario, lets say you bind to
"ViewModel.CurrentPerson.Name.Address.HouseName" on an element. What
happens when the Address field is null or undefined?

Usually with sub observable objects you would use the template binding
passing in a data option and then the template acts on the sub object
itself.

While Andrews example shows you can reference things arbitrarily deep
(you may need to sprinkle some observable value calls e.g.
viewModel.prop().prop().prop().value), it isn't always safe to,
depending on how good you are at controlling your data.

--
-barkmadley
sent from an internet enabled device
http://barkmadley.com

Jon Rimmer

unread,
Jun 27, 2011, 9:57:13 AM6/27/11
to knock...@googlegroups.com
Thanks! That seems like what I'm after. I'll have a good look at it.

Jon Rimmer

unread,
Jun 27, 2011, 10:32:45 AM6/27/11
to knock...@googlegroups.com
Based on my Silverlight experience, I'd hope that any null or undefined value encountered in the property path would cause the binding to evaluate to null and display an equivalent value, e.g. an empty string or empty selection, in the bound element. Since you asked this question though, I'm guessing this will not happen, which is a shame. One of the strengths of data-binding I've found is that you don't have to worry about the actual state of the data in the view model, you just base the declarative part of the binding on whatever shape it will eventually have, and the binding library takes care of mediating between the two sides of the binding, handling cases of null values, etc.

I'll look into the template binding approach you mentioned, it sounds like it may help mitigate some of this.

Thanks,

Jon

Gareth Evans

unread,
Jan 7, 2015, 9:12:47 PM1/7/15
to knock...@googlegroups.com
I've just implemented a very basic function for resolving a path. It should also support null items in the path, and be observable (update when anything in the path changes)
I'd use it sparingly though.

function path(data, bindingPath)
{
    var tokens = bindingPath.split('.');
    var value = data;
    while (tokens.length > 0 && value)
    {
        var oValue = value[tokens.shift()];
        value = ko.isObservable(oValue) ? ko.unwrap(oValue) : oValue;
    }
    return value;    
}

Usage is: data-bind="text: path($data, 'daterange.start')"

Ian Yates

unread,
Jan 8, 2015, 6:27:24 AM1/8/15
to knock...@googlegroups.com
Something like

ViewModel && ViewModel.CurrentPerson && ViewModel.CurrentPerson.Name && ViewModel.CurrentPerson.Name.Address && ViewModel.CurrentPerson.Name.Address.HouseName

will also do the trick.  It will fall out as undefined or null if any path along the way ends up as undefined or null.  You can substitute a default using something like

(ViewModel && ViewModel.CurrentPerson && ViewModel.CurrentPerson.Name && ViewModel.CurrentPerson.Name.Address && ViewModel.CurrentPerson.Name.Address.HouseName) || "My default"

If not using it from within a binding you can use it as the function within a ko.computed to make derived (aka "computed") observable.  It will automatically subscribe to observables as they're accessed and itself fire change notifications when those observables change.

Kevin Van Lierde

unread,
Jan 10, 2015, 8:37:29 PM1/10/15
to knock...@googlegroups.com
You wouldn't necessarily need templates, it can also be done with the with  with: and foreach: binding.
As per the observables, instead of using endless conditionals or nested calls, you could simply initiate them with a default 'blank' value:

this.someDependantProp = ko.computed(function() {return self.otherProp() ||''};// otherProp === ko.observable()
Reply all
Reply to author
Forward
0 new messages