KnockoutJS proper way to update observableArray AJAX
Asked Answered
Q

2

6

In KnockoutJS, what's the proper way to update an observableArray of JSON data each time an AJAX command is run?

Right now, I'm blanking the array using something like viewmodel.items([]), then repopulating it with the JSON data from the server. Short of using the KnockoutJS mapping plugin (which might be the only way to do this) what is the correct path?

My server logic is going to send some of the same data each time, so I can't just iterate and push the items into the array unless I want duplicates.

//// Adding how I'm doing it today ////

I'm not sure why I'm doing it this way, but this is just how I initially figured out how to update. So basically, like I said before, I get JSON data, then I pass it to something like this:

    _model.addIncident = function (json) {
       var checked = json.UserTouches > 0 ? true : false;
       _model.incidents.push({
          id: ko.observable(json.IncidentIDString),
          lastTouchId: ko.observable(json.UserLastTouchIDString),
          weight: ko.observable(json.Weight),
          title: ko.observable(json.Title),
          checked: ko.observable(checked),
          createdOn: ko.observable(json.IncidentCreatedOn),
          servicename: ko.observable(json.Servicename),
          inEdit: ko.observable(false),
          incidentHistory: ko.observableArray(),
          matchScore: ko.observable()
      });
   };

for each node in the JSON array. As you can see, I've got some custom observables in there that get build with every passing piece of data. Maybe this is the wrong way to go, but it's worked great up until now.

Quoin answered 19/3, 2012 at 17:39 Comment(0)
B
10

An observableArray is really just a normal observable with some extra methods for array operations.

So, if you want to set the value of an observableArray to a new array, you can just do:

viewModel.items(myNewArray)

The mapping plugin can help you update the existing items in an array with any updates. In this case, your UI will only be updated from any differences.

Bonilla answered 19/3, 2012 at 18:6 Comment(7)
So, I added an example of what I'm currently doing above. Basically I'm building a custom array for each item in my JSON. If I were to do this, then follow your advice and pump the new array directly into the observable, will Knockout just know to ignore duplicates? Maybe this is a bad structure? Thanks for the quick response...I'm anxious to see how you handle updates like this. I've poured through the Knockout site and I've never really noticed a straightforward solution for this...but that's probably just my fault :)Quoin
If you are using the foreach binding (or template with the foreach parameter), then the UI will update appropriately. It will handle duplicates properly, but keep in mind that an old item { name: 'Bob' } does not equal a new item { name: 'Bob' } unless they are actually a reference to the same item. This is where the mapping plugin is useful along with its key functionality to take updated data and apply it to existing objects. The mapping plugin makes this easy. From what you are doing above, the mapping plugin would likely be a good choice for you.Bonilla
The only problem I see with the mapping plugin is that it blows away my entire current viewmodel in lieu of a dynamically created one. Can you use the mapping plugin to map JSON data to an observableArray inside of my existing model? I'm looking into the mapping plugin, I think you're right it does seem like the way to move forward, I'm just not familiar with it yet.Quoin
I guess a more logical example would be a scenario where I have a UI feature in my ViewModel...something like viewModel.somethingClicked = function(){}. Obviously I've put this here to take advantage of the template "click" binding, but from what I understand, the mapping plugin would require that I move this logic outside of knockout since it's not a part of my JSON data model.Quoin
You can use the mapping plugin to update part of your view model. Something like: ko.mapping.fromJSON(newArrayAsJson, null, viewModel.items). You would use this against an array that you initialized with the mapping plugin like: viewModel.items = ko.mapping.fromJS([]);Bonilla
Awesome! I'm going to try this. I'll mark this as the answer :).Quoin
RP, if you get a chance could you look at #9775453. It's related to this problem, and it's very odd.Quoin
L
-1

I know I'm way too late on this one as I found myself stuck in this situation just recently. We can use a simple Javascript util function as a work-around.

If you have already marked _model.incidents as observableArray, you can do something like this when binding the returned JSON data:

eval("_model.incidents("+JSON.stringify(json)+");");

It worked for me. Hope you have created your observable like this:

_model.incidents = ko.observableArray([]);
Literature answered 20/6, 2013 at 3:45 Comment(1)
Downvoted for reliance on eval. This looks like some weird voodoo. Why not simply _model.incidents(json); ?Phalarope

© 2022 - 2024 — McMap. All rights reserved.