Search code examples
javascriptpolymerweb-component

Polymer computed binding doesn't get recalculated on update


I have a dom-repeat template that generates table rows, where one of the row cells contains a button, that I want to get disabled when the row value is of certain value. So my component has this code, which uses a computed binding to set the value of the button's disabled property:

<template is="dom-repeat" items="{{list}}" as="ticket">
    <tr class$="{{ticket.status}}">
        <td class="actions">
            <paper-icon-button icon="add" on-tap="incTickets"
                disabled="{{noAvailableTickets(ticket)}}">
            </paper-icon-button>
        </td>
        <td>[[ticket.amount]]</td>
        <td>[[ticket.event.title]]</td>
    </tr>
</template>

And in my code I have this method:

noAvailableTickets : function(ticket) {
    return ticket.event.available_tickets <= 0;
}

I can see that the computed binding method is called when the list is first created, once for each item.

Then when I update items, like this:

this.set('list.' + ind + '.amount', newamount);

The computed binding is not recalcuated, and the method is not called again, even though I can see the value actually change in the ticket.amount display in the template.

The Polymer Data Binding entry in the developer's guide has this to say about computed bindings:

the [...] property is bound to the return value of computeFullName, which is recalculated whenever first or last changes. [first and last are the declared arguments for computeFullName]

So under the assumption that the computed binding is not aware of the correlation between the object value, its amount property and how other logic uses this to update the available_tickets property, I've tried to add a notifyPath command with the full updated object, like this:

this.notifyPath('list.' + displayId, ticket);

But that didn't seem to have any effect. What am I missing?


Solution

  • I suspect notifyPath isn't triggering an update in your case because of its dirty check. That is, list[0] and ticket refer to the same instance (despite the fact that the instance's property has changed), so it's not considered "dirty".

    My jsbin experiment demonstrates that setting list[0] to a modified clone indeed triggers an update.

    attached: function() {
      setTimeout(function() {
        var copy = this.list[0].clone();
        copy.amount = 'FREE';
        this.set('list.0', copy);
      }.bind(this), 1000);
    }
    

    But that's probably not the best way to accomplish this.

    According to Polymer docs, to trigger the computed binding whenever a ticket subproperty changes, you would use ticket.* in your binding:

    <paper-icon-button  disabled="{{noAvailableTickets(ticket.*)}}">
    
    Polymer({
      ...
      noAvailableTickets: function(change) {
        var ticket = change.base;
        return ticket.event.available_tickets <= 0;
      }
    });
    

    Here's a jsbin.