Search code examples
javascriptarrayspolymer-1.0mutation

Detect array mutation


I have made a simple proof-of-concept Polymer 1.0 app that demonstrates my problem: JSBin.

In my problem, I am using array mutation methods to alter the array, which contains the list of shopping items.

However, this doesn't seem to work as intended. I do get a change in dom-repeat and when printing the length of the array. But I do not get the change event when I am printing the array itself nor when I wrap it in a function.

In short, why does this work?

<p>Number of items: [[list.length]]</p>

And why does this not work?

<p>Items inline: [[list]]</p>    
<p>Observe function : [[_observe(list)]]</p>

Also, when I uncomment the following line (in the JSBin), things seem to work as indened. But I don't like it since it's a bit hackish.

app.notifyPath('list', app.list.slice());

I have stumbled upon the slice() fix by reading this issue: https://github.com/Polymer/polymer/issues/2068


EDIT

So, after reviewing the comments, the answer to the question "Is this by design" is YES. The array itself doesn't change (since it's only a reference), but it's property do change. That's why the slice() forces the reload since it creates a shallow copy.

However, one could argue whether this is OK. Yes, the variable list does not change per se. But putting [[list]] in the HTML code actually triggers toString(). And result of that function has changed.

I guess I'm stuck with piggybacking the length property for now...


Solution

  • As alluded to in the comments the notifyPath and slice calls are creating a shallow copy of the array and assigning a different reference back to the list variable - triggering an update of the binding. Without maintaining a separate (watchable) variable or messing around with object references, the only other workaround I can think of would be to piggy back on the list.length property instead of the list itself and pass that through some kind of "formatting" function. e.g.

    <p>Items inline: [[format(list.length)]]</p>
    
    app.format = function(){
        return app.list.toString();
    };
    

    » Fiddle


    As pointed out by @zb you could expand on this and make the function reusable with any array by passing the relevant variable as an argument too:

    <p>Items inline: [[format(list, list.length)]]</p>
    
    app.format = function(list){
        return list.toString();
    };
    

    » Fiddle