聚合物和聚合物火: 如何通过观察者循环遍历数据?

3
我有一个简单的<firebase-query>标签,并且我想在通过<dom-repeat>显示之前操纵一些数据。例如,我需要将某些字段转换为链接,并解析一些日期。
所以,我需要在准备好数据后获取数据,循环遍历每个项目,并更改某些值。
为此,我在数据上有一个观察器来检测何时准备就绪。但是,我无法弄清楚如何从JavaScript函数中循环遍历数据。由于某种原因,for(var i in items)不起作用,尽管这些项目确实存在。
这是组件:
<dom-module id="cool-stuff">

  <template>

    <firebase-query id="query" path="/items" data="{{items}}"></firebase-query>

    <template is="dom-repeat" items="{{items}}" as="item">
      [[item.name]]<br />
      [[item.date]]<br />
    </template>

  </template>

  <script>
    Polymer({
      is: 'ix-table',
      properties: {
        items: {type: Object, observer: "_itemsChanged"},
      }
      itemsChanged: function(data) {
        // how do I loop through the data received from firebase-query?
        console.log(data);
      }
    });
  </script>

</dom-module>

理想情况下,我希望在观察者函数中只需要做类似这样的事情:
for(var i in data) {
  obj = data[i];
  obj.name = '<a href="/item/"+obj.key>'+ojb.name+'</a>';
}

但我似乎无法循环遍历这些数据。

在观察函数内部,console.log(data)返回一些奇怪的东西,就像这样:

Array[o]
0: Object (which contains a proper item)
1: Object (same)
2: Object (same)

更新:

这是console.log(data)返回的截图(从观察者内部获取):

enter image description here

该数组似乎已填充了所有对象,但它显示为Array[0]。因此,我无法循环遍历它们。

更新2:

感谢arfost,这是解决方案:

<script>
  Polymer({
    is: 'ix-table',
    properties: {
      items: {type: Object},

    }
    observers: [
      '_itemsChanged(items.splices)'
    ],
    _itemsChanged: function(changeRecord) {
      if (changeRecord) {
        changeRecord.indexSplices.forEach(function(s) {
          for (var i=0; i<s.addedCount; i++) {
            var index = s.index + i;
            var item = s.object[index];
            console.log('Item ' + item.name + ' added at index ' + index);
            // do whatever needed with the item here:
            this.items[index].name = "New name";
          }
        }, this);
      }
    },
  });
</script>

不错,但我认为你在计算索引时犯了一个错误,为什么要加1呢?索引0与对象0匹配,而且已经循环遍历每个对象了,为什么还需要另一个for循环呢? - TheBen
说实话,我并不完全理解这个splice的细节...但我知道这段代码能够工作,并让我能够获取Firebase返回的项目。 - Hubert
我认为如果您的路径以具有嵌套子节点的分支节点结尾,那么您将不需要使用splice,但是当您的路径以直接子节点结束时,则需要使用splice。不过这只是一种参考意见。 - TheBen
我不知道这是否是一个优雅的解决方案,但您先生已经解决了我的问题。谢谢! - luckyging3r
3个回答

4

<firebase-query> 结果

请注意,<firebase-query> 的结果是一个对象数组。假设您的数据库在 /notes/<USER_ID>/ 下包含以下条目:

enter image description here

您的 <firebase-query> 将类似于以下内容:

<firebase-query
    id="query"
    app-name="notes"
    path="/notes/[[user.uid]]"
    data="{{notes}}">
</firebase-query>

(其中user绑定到<firebase-auth>.user。)

假设用户已登录,<firebase-query>将使用以下数组填充其data属性(即绑定到notes):

enter image description here

注意每个对象都包含一个$key属性,该属性对应于在Firebase控制台的数据库视图中看到的项的键。

然后您可以使用<dom-repeat>直接迭代notes

<template is="dom-repeat" items="[[notes]]">
  <li>
    <div>key: [[item.$key]]</div>
    <div>body: [[item.body]]</div>
    <div>title: [[item.title]]</div>
  </li>
</template>

绑定到HTML字符串

请注意,在这种情况下,字符串数据绑定会被字面呈现,因此尝试将name设置为obj.name = '<a href="...">'将呈现字面字符串而不是锚点。相反,你应该在模板中声明标签,并在这些标签内绑定keyname属性。因此,你的观察者可以被替换为以下内容:

<template is="dom-repeat" items="{{items}}" as="item">
  <a href$="/item/[[item.key]]">[[item.name]]</a><br />
  [[item.date]]<br />
</template>

遍历数组

以下提示仅适用于您在显示数据之前更改数据的情况...

当遍历数组时,应该避免使用 for..in,因为它不能保证迭代顺序,并且可能会迭代您并不一定关心的可枚举属性。相反,您可以使用for..of(假设您的应用程序支持ES6):

for (let note of notes) {
  note.title += ' ...';
}

或者 Array.prototype.forEach()

notes.forEach(function(note) {
  note.title += ' ...';
});

非常感谢,这非常有用,我很感激。在我的情况下,我确实需要在显示之前改变数据,并且由于某种原因,我仍然无法从观察者函数中循环遍历数据。我已经在原始帖子中添加了一个截图,显示了观察者中console.log(data)的内容。 - Hubert

2
我认为我遇到了和你一样的问题。
这是由于Firebase查询获取数组的方式、Polymer观察器的工作方式以及JavaScript控制台在显示对象时采用引用方式而隐藏了这一事实所致。
事实上,真正发生的是Firebase查询创建了一个空数组,这会触发你的Polymer观察器。
因此,当数组被创建时,你的函数就会被调用,但它仍然是空的,你无法迭代它,因为它是空的。然后你记录它,在那里基本子属性被正确显示(array [0])。
然后Firebase开始用数据填充数组。数组的引用保持不变,因此Polymer不会再次触发观察器,在控制台中,当它尝试显示数组时,它显示日志中引用的数组,该数组现在包含数据。
我建议你使用一个数组突变观察器来替换你简单的观察器,如下所示。
`properties: {
    items: {type: Object},
  },
,
observers: [
       '_itemsChanged(items.splices)'
],`

每当一个对象被添加到你的数组中时,它都会触发并且你可以完成你所需要的工作 :)
我有一个有关数组变异观察器的文档链接: polymer array mutation observer 希望这能解决你的问题,祝你有美好的一天。

啊,太棒了,那正是解决方案!非常感谢您指出正确的位置。我已经用解决方案更新了第一篇帖子。 - Hubert
我也遇到了这个问题。我不想在每个“splice”上采取行动,而是在全部数据集加载完所有项目后一次性处理。我该怎么做? - Riël
有了 Firebase 的实时能力,您永远不会真正拥有“整个”数据集,因为其他客户端可能随时将新节点推入其中。如果您希望将其视为一个整体而不是 X 个独立元素的列表,则 firebase-document 更适合。 - Arfost
是的,如果你不需要循环遍历每个项(在我的情况下我需要),那么这样做会更简单,使用 Firebase 文档将更合适。 - Hubert

0

我认为没有场景需要通过循环数组来改变数据,而不是使用计算绑定,像这样:

<template is="dom-repeat" items="{{items}}" as="item">
  <child-el date="{{_computeDate(item.date)}}"></child-el><br />
  <child-el attr1="{{_someOtherConversion(item.prop1)}}"></child-el><br />
  <child-el attr2="{{_iPromiseAnyConversionCanBeDoneLikeThis(item.prop2)}}"></child-el><br />
</template>
<script>
  _computeDate: function(item) {
    //do date converstion
}

主要是因为我把数据传递给了一个子组件,在那里以漂亮的表格形式显示。由于子组件是通用的,所以我需要在此之前更改一些数据。 - Hubert
通过计算绑定进行更改 - Ryan Tyler
我修改了我的答案以回应您的最新评论。 - Ryan Tyler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接