user2591612
user2591612

Reputation:

Merge value from another observableArray into existing observableArray in KnockoutJS

I am having trouble figuring out how to add to a pre-existing observableArray after issuing a second AJAX request for similar, but different data.

My first JSON request does not contain the type called value, so I need to add it. My initial JSON structure:

[{"statusmsg":"OK","data":{"status":"stopped"},"sender":"hostname","statuscode":0}]

A second AJAX request is issued which gets the uptime of a service from the REST server:

[{"statusmsg":"OK","data":{"value":"","fact":"some_fact"},"sender":"hostname","statuscode":0}

I want to add to the nested object data the type value, which contains some_fact.

I have tried the following:

ko.utils.arrayForEach(self.rows(), function(row) {
   ko.utils.arrayForEach(self.timeRows, function(time) {
      if(row.sender() == time.sender()) {
         self.rows()[0].data.push({value: time.data.value()});
      }
   });
});

Note: I am using the placeholder index 0 for testing only. Ideally, I would like to just find the match based on the hostname and update the observableArray.

When this code executes, I receive the error that

TypeError: self.rows(...)[0].data.push is not a function

I also tried putting parentheses next to data like data().push, but that did not work either.

The intent is to add a new value to the original observableArray called rows so I can update my view with the new information.

Upvotes: 1

Views: 632

Answers (2)

user2591612
user2591612

Reputation:

To solve my problem, I used @Sybeus' answer but then I realized that my rows were not updating in my UI because my UI was bound to the old version of rows. Even when I tried to create a dummy subscriber and notify my computed observable that the rows were updated, it was still bound to the old rows as described in this answer:

If you would want to replace the array you should probably create a replacement array, push tweets into that array and finally replace the array in the observableArray by doing self.tweets(replacementArray);.

So what I had to do was the following:

self.updateServiceStartTime = function() {
  var url = "url";

  $.ajax({
     method: "GET",
     url: url,
     success: function(data) {
        var observableData = ko.mapping.fromJSON(data);
        var array = observableData();
        self.timeRows(array);

        ko.utils.arrayForEach(self.rows(), function(row) {
           ko.utils.arrayForEach(self.timeRows(), function(time) {
              if(row.sender() == time.sender()) {                    
                 if (time.data.value()) {
                    row.data.value = time.data.value;
                 }
                 else {
                    row.data.value = "No time found yet.";
                 }
              }
           });
        });
        self.cloneRows(self.rows().slice());
        self.rows.removeAll();
        self.rows(self.cloneRows().slice());
     }
  });
  };

This updates the UI with the new data that I added.

Upvotes: 0

Sybeus
Sybeus

Reputation: 1189

Since you've opted for the default Knockout mapping, it automatically assumes an associative array is a simple object with attributes, and thus has no array prototype functions, such as push(). You don't need the index reference either, since you've already found the row in self.rows(). So just simply set the value attribute of the 'data' sub-object of the matching row object:

ko.utils.arrayForEach(self.rows(), function(row) {
   ko.utils.arrayForEach(self.timeRows, function(time) {
      if(row.sender() == time.sender()) {
         row.data.value = time.data.value;
      }
   });
});

Upvotes: 3

Related Questions