I am making a page that displays a list of items. The page lists the items newest to oldest, and categorizes them first by year then by month. It would look something like the following example:
March
February
October
May
A simplified version of the HTML is:
<template is="dom-repeat" items="{{years}}" as="year">
<span>[[year.name]]</span>
<div id="same-year-items>
<template is="dom-repeat" items="{{year.months}}" as="month">
<span>[[month.name]]</span>
<div id="same-month-items">
<template is="dom-repeat" items="{{month.items}}" as="item">
<custom-list-item item="{{item}}"></custom-list-item>
</template>
</div>
</template>
</div>
</template>
In the Javascript file I define a years
array property. Each year in years
contains a months
array, and each month in months
contains an items
array (see the dom-repeat elements). I query a database for some list items, loop through each item, and then add it to the corresponding month array.
This page only loads the first n items. I want to have a "load more" button at the bottom which loads the next n items. (Yes, I know. Infinite scrolling is probably better--that will come later. Even if I were to use infinite scrolling I would still have the issue I'm about to describe.)
When I load more items, the page doesn't update to show the new items. I know Polymer is very particular about how you bind data, especially when it involves sub properties. This is a simplified version of the Javascript which deals with setting the new items to the arrays:
setItems: function(newItems) {
var tempYearsArray = [];
for (var i = 0, i < this.years.length; i++) {
tempYearsArray.push(this.years[i]);
}
this.set('years', []);
for (var j = 0; j < newItems.length; j++) {
var item = newItems[j];
this.setMonthAndYear(item, tempYearsArray);
}
this.set('years', tempYearsArray);
}
setMonthAndYear: function(item, tempYearsArray) {
var itemDate = new Date(item.lastModified);
var currentYear = itemDate.getFullYear();
var monthIndex = itemDate.getMonth();
var monthName = this.monthNames[monthIndex];
var newYear = true;
var newMonth = true;
for (var i = 0; i < tempYearsArray.length; i++) {
var year = tempYearsArray[i];
if (currentYear === year.name) {
newYear = false;
for (j = 0, j < year.months.length; j++) {
var month = ref[j];
if (monthName === month.name) {
month.items.push(item);
newMonth = false;
break;
}
}
if (newMonth) {
var month = {
items: [item],
name: monthName
};
year.months.push(month);
}
break;
}
}
if (newYear) {
var month = {
items: [item],
name: monthName
};
year = {
name: currentYear,
months: [month]
};
return tempYearsArray.push(year);
}
}
I can see that years
has the new items, but the page still doesn't render them. I've read other posts with similar problems, which is where I got the idea of this.set('years', [])
and the temporary array. I've also tried doing stuff like this.notifyPath('year.months', year.months.slice())
and this.notifyPath('month.items', month.items.slice())
. I'm not sure why this would work, but I saw that suggested in this Github issues post.
Also if you have a better suggestion than a years array which contains a months array which contains the item array, all the better.
From what I can tell, you're changing the existing array, which means that Polymer doesn't see any difference between your "changed" array and the previous one ... because they are both the same array (array references, yay!).
You need to either 1) create a new array and let it replace the new one with this.set()
, 2) clone the array – through JSON.parse(JSON.stringify(arr)
– that you modified and this.set()
it, 3) Use this.splice()
because that should, in theory, work, or 4) do what you did: reset the array with an empty one and then replace it with your modified array.
Array subproperty updates in dom-repeat is an issue that is fixed in Polymer 2 and beyond, because they removed the dirty checking.
Another small thing, but don't create variables inside for loops. It's a minimal time loss to create new variables inside a for loop, instead of creating one outside that you then reuse inside the loop.
// Your code
for (var j = 0; j < newItems.length; j++) {
var item = newItems[j];
this.setMonthAndYear(item, tempYearsArray);
}
// My suggestion
var item;
for (var j = 0; j < newItems.length; j++) {
item = newItems[j];
this.setMonthAndYear(item, tempYearsArray);
}