SteveSanderson / knockout-es5

Knockout.js meets ECMAScript 5 properties
158 stars 39 forks source link

How do you get an observable within an observableArray? #51

Open grofit opened 8 years ago

grofit commented 8 years ago

So for normal objects you would do something like ko.getObservable(myModel, myPropertyName); and that works great, however lets assume I have a model like so:

var someModel = {
   arrayData = [22, 21]
}

I have tried the following:

var observableArray = ko.getObservable(someModel, "arrayData");
var observableIndex0 = observableArray()[0];
// observableIndex0 is the value not the observable
var observableIndex0 = ko.getObservable(someModel.arrayData, 0);
// observableIndex0 is null 
var observableIndex0 = ko.getObservable(someModel.arrayData, "0");
// observableIndex0 is null 
var observableIndex0 = ko.getObservable(someModel.arrayData[0]);
// observableIndex0 is null 
var observableArray = ko.getObservable(someModel, "arrayData");
var observableIndex0 = ko.getObservable(observableArray, 0);
// observableIndex0 is null 
var observableArray = ko.getObservable(someModel, "arrayData");
var observableIndex0 = ko.getObservable(observableArray, "0");
// observableIndex0 is null 

So is there some other way I am meant to get access to the observable objects within the array?

mbest commented 8 years ago

Are you sure there are observable objects within the array?

grofit commented 8 years ago

that is an interesting question and not as easy to answer as I would like.

I am going to say yes, but with some assumptions:

1) When a viewmodel is tracked (i.e ko.track(myModel);) all elements in an array become observables

2) When an element is added to an array which is being tracked, this will create an observable behind the scenes

If those assumptions are wrong then this would probably be part of the problem.

mbest commented 8 years ago

The answer is that array items are not made observable, even when the deep option is used.

grofit commented 8 years ago

:( thats a major show stopper for our use case then, is there a way to get this behaviour? the example above is trivial but in a more realistic use case we would have things like: (its in typescript btw)

class Item
{
   public Name = "";
   public Weight = "";
}

class Inventory
{
   public Items: Array<Item>;
}

class Character
{
   public Inventory: Inventory;
}

So when we track the character we would want it to automatically track the child objects so when we display all items and the details are changed, this would update the Item entries, there is also the concerns of validation in the real world, but currently this post-processes the model and uses ko.getObservable to walk the tree.

Anyway with the above use case in mind, would you say there is a way to achieve child observables without having to manually track at each level?

grofit commented 8 years ago

Just a quick bump on this, is there anything in the roadmap to automatically turn arrays into observable arrays etc?

If this is not on the cards at any point then that is fine, but it has become a blocker in my current project so I could do with knowing either way.

onlyurei commented 8 years ago

@grofit check out https://github.com/nathanboktae/knockout-es5-option4

grofit commented 8 years ago

Thanks, I had seen this one before but it appeared to do full object replacement from what the examples showed, rather than object augmentation, not that I have any strong feelings to either.

I would say though that current ko-es5 is unfortunately not a complete replacement for using normal knockout, so unless this is addressed (which I think someone is working on in #37) I guess the only option if you don't (like me) want any ko.* calls in your models you would need to move over to the other one.

jvissers commented 8 years ago

I'm now curious as to whether this plugin provide an actual benefit over 'normal' ko. I really like the cleaner code of es5, but it needs to be a complete solution. Thoughts?

grofit commented 8 years ago

@jvissers I agree that it should be a complete replacement solution for ko.observable/observableArray types, but unfortunately it is not. I have had to stop using it although I much preferred having the cleaner models but as you cannot do much complex without array support I have had to drop it and just return to the normal observable route.

jvissers commented 8 years ago

@grofit - thank you for replying. I've went back to the blogpost that announced this feature. http://blog.stevensanderson.com/2013/05/20/knockout-es5-a-plugin-to-simplify-your-syntax There it reads:

By design, ko.track does not recurse into child objects. I would encourage you to declare child objects as instances of some class of your own, with its constructor having its own ko.track call — this gives you far more control over how much of the object graph is walked.

I'm not sure - but this seems to suggest that it was a design decision to have the plugin operate as it does right now. See also this: [http://jsfiddle.net/2Epfp/25/]. Is my interpretation correct?

jvissers commented 8 years ago

@SteveSanderson , @grofit --- I've just watched a presentation of Steve where he actually uses this ES5 plugin (https://www.youtube.com/watch?v=MNiUcuo3Wio). This seems to suggest that the plugin is production quality and it works as designed.

Would be nice to hear if there are any real caveats here. Thanks.

jvissers commented 8 years ago

The behavior is by design as the code documentation tells us:

// By design, this does not recursively walk child object properties, // because making literally everything everywhere independently observable is usually // unhelpful. When you do want to track child object properties independently, // define your own class for those child objects and put a separate ko.track call // into its constructor --- this gives you far more control.

grofit commented 8 years ago

sure but this approach makes one of the key reasons for using knockout es5 moot, as the general use case for knockout-es5 is so that your models are not dependent on knockout and have no knowledge of it. The moment you introduce ko.track(this); into the constructor you may as well have just used basic knockout.

I do agree to some degree that you may not want to assume that by default everything nested wants to be observed, but at the same time you cannot assume that it doesnt want to be, as there is an option which already exists (deep or something) it would make sense if this is set then it should trawl the entire tree.