Subscribe to observable array for new or removed entry only
Asked Answered
W

6

76

So yes I can subscribe to an observable array:

vm.myArray = ko.observableArray();
vm.myArray.subscribe(function(newVal){...});

The problem is the newVal passed to the function is the entire array. Is there anyway I can get only the delta part? Say the added or removed element?

Weathered answered 4/1, 2013 at 0:41 Comment(0)
D
130

As of KnockoutJS 3.0, there's an arrayChange subscription option on ko.observableArray.

var myArray = ko.observableArray(["Alpha", "Beta", "Gamma"]);

myArray.subscribe(function(changes) {

    // For this example, we'll just print out the change info
    console.log(changes);

}, null, "arrayChange");

myArray.push("newitem!");

In the above callback, the changes argument will be an array of change objects like this:

[ 
   { 
      index: 3, 
      status: 'added', 
      value: 'newitem!' 
   }
]

For your specific problem, you want to be notified of new or removed items. To implement that using Knockout 3, it'd look like this:

myArray.subscribe(function(changes) {

    changes.forEach(function(change) {
        if (change.status === 'added' || change.status === 'deleted') {
            console.log("Added or removed! The added/removed element is:", change.value);
        }
    });

}, null, "arrayChange");
Donatello answered 31/10, 2013 at 16:42 Comment(5)
what is the status for a 'modified' individual?Yoke
I don't understand what you're asking when you say, "modified individual".Donatello
individual == instance of changes == change ['added','deleted','???','???',...???]Yoke
If you're asking what the notification is if an individual item was changed, one of the items in the array, the answer is none. You'll only be notified of removals and additions.Donatello
the documentation indicates that moving items will also signal an added or deleted change myArray.reverse(); // [{ index: 0, moved: 1, status: 'deleted', value: 'Alpha' },{ index: 1, moved: 0, status: 'added', value: 'Alpha' }] maybe there should be a check for added and deleted status without the move property?Houri
L
7

Since I couldn't find any info on this elsewhere, I'll add a reply for how to use this with TypeScript.

The key here was to use the KnockoutArrayChange interface as TEvent for subscribe. If you don't do that, it'll try to use the other (non-generic) subscribe and will complain about status, index, and value not existing.

class ZoneDefinition {
    Name: KnockoutObservable<String>;
}

class DefinitionContainer
{
    ZoneDefinitions: KnockoutObservableArray<ZoneDefinition>;
    constructor(zoneDefinitions?: ZoneDefinition[]){
        this.ZoneDefinitions = ko.observableArray(zoneDefinitions);
        // you'll get an error if you don't use the generic version of subscribe
        // and you need to use the KnockoutArrayChange<T> interface as T
        this.ZoneDefinitions.subscribe<KnockoutArrayChange<ZoneDefinition>[]>(function (changes) {
            changes.forEach(function (change) {
                if (change.status === 'added') {
                    // do something with the added value
                    // can use change.value to get the added item
                    // or change.index to get the index of where it was added
                } else if (change.status === 'deleted') {
                    // do something with the deleted value
                    // can use change.value to get the deleted item
                    // or change.index to get the index of where it was before deletion
                }
            });
        }, null, "arrayChange");
}
Lassa answered 29/12, 2013 at 18:36 Comment(0)
C
1

In order to only detect push() and remove() events, and not moving items, I put a wrapper around these observable array functions.

var trackPush = function(array) {
    var push = array.push;
    return function() {
        console.log(arguments[0]);
        push.apply(this,arguments);
    }
}
var list = ko.observableArray();
list.push = trackPush(list);

The original push function is stored in a closure, then is overlayed with a wrapper that allows me do do anything I want with the pushed item before, or after, it is pushed onto the array.

Similar pattern for remove().

Celestyn answered 23/2, 2015 at 15:10 Comment(0)
W
0

I am using a similar but different approach, keep track whether an element has been instrumented in the element itself:

myArray.subscribe(function(array){
  $.each(array, function(id, el) {
    if (!el.instrumented) {
      el.instrumented = true;
      el.displayName = ko.computed(function(){
        var fn = $.trim(el.firstName()), ln = $.trim(el.lastName());
        if (fn || ln) {
          return fn ? (fn + (ln ? " " + ln : "")) : ln;
        } else {
          return el.email();
        }
      })
    }
  });
})

But it is really tedious and the pattern repeated across my code

Weathered answered 4/1, 2013 at 1:15 Comment(1)
To save yourself a property, you could use: if(!( 'displayName' in el)){ }Histiocyte
B
-1

None that I know of. Wanna know what I do? I use a previous variable to hold the value, something called selectedItem

vm.selectedItem = ko.observable({});
function addToArray(item) { vm.selectedItem(item); vm.myArray.push(item); }

So that way, when something happens to my observable array, I know which item was added.

vm.myArray.subscribe(function(newArray) { var addedItem = vm.selectedItem(item); ... }

This is really verbose, and assuming your array holds many kinds of data, you would need to have some sort of flags that helps you know what to do with your saved variables...

vm.myArray.subscribe(function(newArray) {
  if ( wasUpdated )
    // do something with selectedItem
  else
    // do whatever you whenever your array is updated
}

An important thing to notice is that you might know which item was added if you know whether push or unshift was used. Just browse the last item of the array or the first one and voila.

Ballerina answered 4/1, 2013 at 1:0 Comment(1)
I am using a different approach: keep track whether an element has been instrumented in the element itself. See my answer aboveWeathered
O
-2

Try vm.myArray().arrayChanged.subscribe(function(eventArgs))

That has the added value when an item is added, and the removed value when an item is removed.

Ob answered 18/9, 2013 at 21:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.