Patrick Cornelissen
Patrick Cornelissen

Reputation: 7958

Change notification for map of arrays in nested dom-repeat

I’m trying to add something to a map of arrays and the value binding does not pick up the change :-(

I have something like this:

       properties: {
            deals: {
                type: Object,
                notify: true,
                value: function () {
                    return {
                        items: {
                            "MONDAY": [],
                            "TUESDAY": [],
                            "WEDNESDAY": [],
                            "THURSDAY": [],
                            "FRIDAY": [],
                            "SATURDAY": [],
                            "SUNDAY": []
                        }
                    }
                },
            },
            weekDays: {
                type: Array,
                value: function () {
                    return [
                        {"text": "Montag", "key": "MONDAY"},
                        {"text": "Dienstag", "key": "TUESDAY"},
                        {"text": "Mittwoch", "key": "WEDNESDAY"},
                        {"text": "Donnerstag", "key": "THURSDAY"},
                        {"text": "Freitag", "key": "FRIDAY"},
                        {"text": "Samstag", "key": "SATURDAY"},
                        {"text": "Sonntag", "key": "SUNDAY"}
                    ]
                },
            },
        },

And then I have nested dom-repeat to render the elements. This works when I use a function like

        _itemsForDay: function (day,map) {
            const data = map[day.key];
             return data;
        }

But when I add elements it’s not redrawn and I have tried many notifyPath variants, but it doesn’t render the new elements :-(

This is what I tried combined into one:

  add: function (e) {
            day = e.model.day.key;
            this.push("deals.items." + day,
                {
                    description: "Bla",
                    price1: "23,45€",
                    price2: "43,21€",
                    imageId: 1
                }
            );
            this.notifyPath("weekDays");
            this.notifyPath("weekDays.*");
            this.notifyPath("weekDays.splice");
            this.notifyPath("deals.items.*");
            this.notifyPath("deals.items.*");
            this.notifyPath("deals.items.splice");
            this.notifyPath("deals.items."+day);
            this.notifyPath("deals.items."+day+".*");
            this.notifyPath("deals.items."+day+".splice");
            this.notifyPath("weekDays.*");
            console.log("Length: "+this.deals.items[day].length)
        },

How can I make sure that the content of the map key is rerendered (also the whole map can be rerendered, I don’t care).

The template looks like this:

       <tr>
            <template is="dom-repeat" items="{{weekDays}}" as="day" >
                <td>
                    <template is="dom-repeat" items="{{_itemsForDay(day,deals.items)}}" >
                        <div>
                            DESC: [[item.description]]
                            IMG: [[item.imageId]]
                            [[deals.price1Label]]: [[item.price1]]
                            [[deals.price2Label]]: [[item.price2]]
                        </div>
                    </template>
                    <paper-button on-click="add" data-day="[[day.key]]">
                        +
                    </paper-button>
                </td>
            </template>
        </tr>

Interesting is that when I remove the outer loop and use fixed weekdays, it works:

              <td>
                    <template is="dom-repeat" items="{{deals.items.TUESDAY}}" >
                        <div>
                            DESC: [[item.description]]
                            IMG: [[item.imageId]]
                            [[deals.price1Label]]: [[item.price1]]
                            [[deals.price2Label]]: [[item.price2]]
                        </div>
                    </template>
                    <paper-button on-click="add" data-day="TUESDAY">
                        +
                    </paper-button>
                </td>

This picks up the changes and renders the new items, but then I have to duplicate the inner part a few times, which is far from ideal.

Upvotes: 3

Views: 90

Answers (2)

Patrick Cornelissen
Patrick Cornelissen

Reputation: 7958

ZeeWolf in the slack channel had a nice idea, which worked:

Create a new property:

                dayLists: {
                    type: Array,
                    computed: '_computeDayLists(deals.items.*)'
                }

Add compute method:

        _computeDayLists: function() {
            return this.weekDays.map(day => {
                const dayKey = day.key;
                const items = this.deals.items[dayKey];
                console.log("Found for "+dayKey+" #"+items.length+" items");
                return {
                    key: dayKey,
                    items: items
                }
            });
        },

Update template:

           <template is="dom-repeat" items="{{dayLists}}" >
                <td>
                    <template is="dom-repeat" items="{{item.items}}" mutable-data>
                        <div>
                            DESC: [[item.description]]
                            IMG: [[item.imageId]]
                            [[deals.price1Label]]: [[item.price1]]
                            [[deals.price2Label]]: [[item.price2]]
                        </div>
                    </template>
                    <paper-button on-click="add">
                        +
                    </paper-button>
                </td>
            </template>
        add: function (e) {
            const day = e.model.item.key;
            this.push("deals.items." + day,
                {
                    description: "Bla",
                    price1: "23,45€",
                    price2: "43,21€",
                    imageId: 1,
                    new: true
                }
            );
            console.log("Length: " + this.deals.items[day].length)
        },

Explanation

The inner loop somehow didn't pick up the changes, so this approach uses the computed list which has a dependency on the map items content (the .* enables this). This redraws the list when something changed in there. Important is the mutable-data without it, that doesn't work!

And make sure that you have a recent version of polymer. I had an older version that wasn't updated due to a misconfigured bower maven step and it didn't work until I updated the dependencies.

Upvotes: 1

codeMonkey
codeMonkey

Reputation: 4815

I have encountered this problem too, grrr! Try this.

Make a new computed property

static get properties() {
    return {
        ...
        'dayDeals': {
            type: Array,
            computed: 'computeDayDeals(deals.items)'
        }
    }
}

computeDayDeals(deals.items) {
    return Object.keys(deals.items).map(x => { day: x, deals: deals.items[x].deals });
}

Change _itemsForDay(day,deals.items) to _itemsForDay(day, dayDeals.*) and make like this:

_itemsForDay(day, dayDeals) {
    return !!dayDeals.base ? dayDeals.base.find(x => x.day == day.key).deals : [];
}

Upvotes: 0

Related Questions