将项目添加到子数组时,聚合物嵌套 dom-重复模板不会更新

Polymer nested dom-repeat templates are not updating when adding items to sub array

上下文

我正在制作一个显示项目列表的页面。该页面列出了从最新到最旧的项目,并首先按年份然后按月份对它们进行分类。它看起来类似于以下示例:

2019

三月

二月

2018

十月

五月

HTML 的简化版本是:

<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>

在 Javascript 文件中我定义了一个 years 数组 属性。 years 中的每一年都包含一个 months 数组,months 中的每个月都包含一个 items 数组(参见 dom-repeat 元素)。我在数据库中查询一些列表项,遍历每个项目,然后将其添加到相应的月份数组中。

问题

此页面仅加载前 n 项。我想在底部有一个“加载更多”按钮,用于加载下一个 n 项。 (是的,我知道。无限滚动可能更好——稍后会出现。即使我使用无限滚动,我仍然会遇到我要描述的问题。)

当我加载更多项目时,页面不会更新以显示新项目。我知道 Polymer 对如何绑定数据非常讲究,尤其是当它涉及子属性时。这是 Javascript 的简化版本,用于将新项目设置到数组:

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);
  }
}

我可以看到 years 有新项目,但页面仍然没有呈现它们。我读过其他有类似问题的帖子,这就是我想到 this.set('years', []) 和临时数组的地方。我也尝试过 this.notifyPath('year.months', year.months.slice())this.notifyPath('month.items', month.items.slice()) 之类的东西。我不确定为什么会这样,但我在 this Github issues post.

中看到了建议

此外,如果您有比包含项目数组的月份数组更好的建议,那就更好了。

我成功了。它不像 Polymer 那样优雅或惯用,但它确实有效,所以我在这里分享它。

我制作了一个 items 属性 来存储所有项目。然后当我加载一批新的项目时,我将新项目推送到现有项目数组并再次重建整个 years 数组。

据我所知,您正在更改现有数组,这意味着 Polymer 看不出您的 "changed" 数组与前一个数组有任何区别...因为它们都是相同的数组(数组引用,耶!)。

您需要 1) 创建一个新数组并用 this.set() 替换新数组,2) 克隆您修改过的数组 – 通过 JSON.parse(JSON.stringify(arr)this.set() 它,3) 使用 this.splice() 因为理论上应该可以工作,或者 4) 做你所做的:用一个空数组重置数组,然后用修改后的数组替换它。

dom-repeat 中的数组子属性更新是 Polymer 2 及更高版本中已修复的问题,因为它们删除了脏检查。


另一件小事,但不要在 for 循环内创建变量。在 for 循环内创建新变量的时间损失最小,而不是在外部创建一个然后在循环内重复使用的变量。

    // 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);
  }