MandoMando
MandoMando

Reputation: 5515

Is there a way to get a callback when an array item value has changed?

This may seem silly, but in this day and age, one should be able to expect JS to raise an event if contents of an array have changed.

A few questions been asked regarding getting notified when a variable changes (define getter or setter). And there seems to be a way to do that (at least for most browsers including IE6+)

My issue is that I'm trying to get notified if an item inside an array changes:

    var ar = ["one", "two", "three"];
    // setting the whole array will call the custom setter method
    // (assuming you defined it)

    ar = ["one", "three", "five"];

    // however, this will only call the getter method and won't call the setter
    // without defining custom setters for every item in the array.  

    ar[1] = "two";

Obviously, I'm trying to avoid forcing the coder to use old-school Java style .getVale() and .setValue() functions to access/modify data.

Upvotes: 5

Views: 2894

Answers (3)

Alireza Mirian
Alireza Mirian

Reputation: 6692

I think timeout-based solutions are not the best.
If you can only use push and pop to modify your array, you can override push and pop methods of Array prototype (or only some object that you want to monitor):

var myWatchableArr = [];
myWatchableArr.setChangeCallback = function(callback){
    this.changeCallback = callback;
}
myWatchableArr.push = function(e){
    Array.prototype.push.call(this,e);
    if(typeof this.changeCallback == "function")
      this.changeCallback(this);
}
myWatchableArr.push(3);
myWatchableArr.setChangeCallback(function(arr){
    console.log("the array has been changed", arr);
});
// now watching for changes
myWatchableArr.push(4);

If push and pop are not sufficient, you can add some setAt method to use like myWatchableArr.setAt(3, value) instead of myWatchableArr[3]=value.

Upvotes: 1

MandoMando
MandoMando

Reputation: 5515

Ok, based on @David Wolever's code and other comments, there actually is a solution:

Use the notes from John Dyer to implement the addProperty method. Place a setTimeout in the getter method to compare with original value a short time after the read takes place:

addProperty(myObject, 'vals',
    function () {
        var _oldVal = "" + this._val;
        var _parent = this;
        console.log('getter!');
        setTimeout(function () {
            var curVal = "" + _parent._val;
            if (curVal != _oldVal)
                console.log('array changed!');
        }, 200);
        return this._val;
    },
    function (value) {
        console.log('setter!');
        this._val = value;
    });

    myObject.vals = ["one", "two", "three"];
    myObject.vals[1] = "five";

Upvotes: 0

David Wolever
David Wolever

Reputation: 154594

In short: no, you can't. You'll notice that Arrays don't provide any event dispatching mechanism, and their API doesn't include any callback type functionality.

In longer: as others have noted, it is possible to wrap the array… And it's also possible to poll the arrays contents:

function watchArray(arr, callback) {
    var oldVal = "" + arr;
    setInterval(function() {
        var curVal = "" + arr;
        if (curVal != oldVal) {
            callback();
            oldVal = curVal;
        }
    }, 100);
}

But this method has some obvious problems: it polls, it'll get slow to watch a bunch of arrays, etc.

Upvotes: 3

Related Questions