Subscribe to observable array for new or removed entry only
Solution 1
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");
Solution 2
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");
}
Solution 3
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()
.
Gelin Luo
A Java programmer since Year 2000 Author of ActFramework and Rythm Template Engine
Updated on July 05, 2022Comments
-
Gelin Luo almost 2 years
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? -
Gelin Luo over 11 yearsI am using a different approach: keep track whether an element has been instrumented in the element itself. See my answer above
-
beauXjames over 10 yearswhat is the status for a 'modified' individual?
-
Judah Gabriel Himango over 10 yearsI don't understand what you're asking when you say, "modified individual".
-
beauXjames over 10 yearsindividual == instance of changes == change ['added','deleted','???','???',...???]
-
Judah Gabriel Himango over 10 yearsIf 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.
-
Paul almost 10 yearsthe 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? -
GrantDG over 8 yearsTo save yourself a property, you could use: if(!( 'displayName' in el)){ }