如何使用Polymer检测数组中对象属性的变化?

时间:2015-03-20 08:10:18

标签: google-chrome polymer object.observe

假设我想用Polymer创建一个待办事项列表(v0.5.5)。在我的元素中,我定义了一个属性tasks,它是一个包含{ name: 'foo', done: false }之类对象列表的数组。我想显示剩余任务的数量,因此我需要检测数组中包含的对象的属性done何时更改。

以下是代码摘录:

<polymer-element name="todo-list">
  <template>
    <span>You have {{ tasks.length }} tasks, {{ remaining }} remaining</span>
    ...
  </template>
  <script>
      Polymer({
          tasks: [
              {name: "foo", done: false},
              {name: "bar", done: true}
          ],
          get remaining() {
              return this.tasks.filter(function(t) { return !t.done }).length;
          }
          changeState: function(e) {
              var _task = this.tasks[e.target.getAttribute('data-task-id')];
              _task.done = !_task.done;
          }
      });
  </script>
</polymer-element>

使用Firefox,它正在运行,但不适用于Chrome(41.x)。实际上,Chrome只会检测阵列本身的变化(例如,如果我添加新任务,剩余计数会正确更新)。

我该怎么做?

由于


编辑,关于安迪回答

当我做那种事情时:

var tasks = tasks: [
          {name: "foo", done: false},
          {name: "bar", done: true},
          {name: "baz", done: false}
      ];
Object.observe(tasks, function() { alert('Modification'); }

如果我在数组本身进行修改(如在tasks.push({...})中),则会显示一个弹出窗口。但是,如果我更改数组中包含的对象的属性(例如tasks[0].done = true),则不会发生任何事情。这是我问题的根源......

3 个答案:

答案 0 :(得分:1)

我害怕我不明白这个问题,罗曼。

我已使用以下代码进行测试:

<polymer-element name="my-component" attributes="status count">
  <template>
    <style>
    </style>
    <div >
      <h1>You have {{ tasks.length }} tasks, {{ remaining }} remaining</h1>

      <div 
        style="border: solid 1px red; padding: 20px; border-radius: 20px; display: inline-block;"
        on-click="{{ doTask }}"
      >
        Click to mark as done
      </div>

      <div>
        {{ tasks[0].done }}
      </div>
    </div>
  </template>
  <script>
  Polymer("my-component", {
    tasks: [
              {name: "foo", done: false},
              {name: "bar", done: true}
          ],
    get remaining() {
              return this.tasks.filter(function(t) { return !t.done }).length;
          },
    doTask: function() {
      this.tasks[0].done = true;
    }
  });
</script>
</polymer-element>

当我点击按钮时,标签的值会发生变化,即remining getter会检测到更改。我在Linux上测试了Chromium 41和Firefox。

您可以在http://embed.plnkr.co/HXaKsQHjchqwe0P3bjy5/preview

上测试我的代码

您能不能给我更多关于您想做什么以及它与我的代码有什么不同的信息?

修改

通过Twitter与@romaintaz交谈之后,似乎问题只发生在按钮位于模板内部时,如下所示:

<div >
  <h1>You have {{ tasks.length }} tasks, {{ remaining }} remaining</h1>

  <template repeat="{{ task, taskIndex in tasks }}">

    <template if="{{task.done}}">
      <button 
        style="border: solid 1px red; padding: 20px; border-radius: 20px; display: inline-block;"
        on-click="{{ doTask }}"
      >Click 1</button>
    </template>

    <template if="{{!task.done}}">
      <button 
        style="border: solid 1px red; padding: 20px; border-radius: 20px; display: inline-block;"
        on-click="{{ doTask }}"
      >Click 2</button>
    </template>

  </template>

  <div>
    {{ tasks[0].done }}
  </div>

在这种情况下,removing getter不再检测其中一个列表项属性的更改。

目前我只有一个 quick'n'dirty 解决方案:不是只更改列表项的一个属性,而是更改整个列表项,然后getter看到它。

示例:

<polymer-element name="my-component" attributes="status count">
  <template>
    <style>
    </style>
    <div >
      <h1>You have {{ tasks.length }} tasks, {{ remaining }} remaining</h1>

      <template repeat="{{ task, taskIndex in tasks }}">

        <template if="{{task.done}}">
          <button 
            style="border: solid 1px red; padding: 20px; border-radius: 20px; display: inline-block;"
            on-click="{{ doTask }}"
          >Click 1</button>
        </template>

        <template if="{{!task.done}}">
          <button 
            style="border: solid 1px red; padding: 20px; border-radius: 20px; display: inline-block;"
            on-click="{{ doTask }}"
          >Click 2</button>
        </template>

      </template>

      <div>
        {{ tasks[0].done }}
      </div>
    </div>
  </template>
  <script>
  Polymer("my-component", {
    tasks: [
              {name: "foo", done: false},
              {name: "bar", done: true}
          ],
    get remaining() {
              return this.tasks.filter(function(t) { return !t.done }).length;
          },
    doTask: function() {
      tmp = this.tasks[0];
      tmp.done = true
      this.tasks[0] = {};
      this.tasks[0] = tmp;
    },
    observe: {
      tasks: 'validate'
    }, 
    validate: function(oldValue, newValue) {
    }
  });
</script>
</polymer-element>

Plunkr:http://embed.plnkr.co/YgqtKgYRaRTZmoKEFwBy/preview

答案 1 :(得分:1)

只需2美分

实际上,问题与模板中按钮的存在无关。 您只需使用列表上的重复模板来重现该问题。 下面的代码演示了它。

    

  <h1>You have {{ tasks.length }} tasks, {{ remaining }} remaining</h1>

  <button
    style="border: solid 1px red; padding: 20px; border-radius: 20px; display: inline-block;"
    on-click="{{ doTask }}"
    >
    Click to mark as done
  </button>

  <ul>
    <template repeat="{{ task in othertasks }}">
      <li>{{task}}</li>
    </template>
  </ul>

  <div>
    {{ tasks[0].done }}
  </div>
</template>
<script>
  Polymer({
    tasks: [
      {name: "foo", done: false},
      {name: "bar", done: true}
    ],
    othertasks: ["foo","bar"],        
    get remaining() {
      return this.tasks.filter(function(t) { return !t.done }).length;
    },
    doTask: function () {
      this.tasks[0].done = true;         
    }
  });
</script>
</polymer-element>

我实际上觉得它很自然。当我们查看规范Object.observe()时。实际上,在数组上,只有在我们:

时才触发它
  • 删除元素
  • 添加元素
  • 修改项目(我们在这里说 参考变更)

如果更改数组中元素的内部属性,则不会触发。 如果我们在ready方法中添加一个监听器,我们将看到它不是由我们的doTask方法触发的。这就是为什么Horacio的黑客行之有效的原因。他取代了这个物体。 另一种解决方案是使用

手动通知更改
Object.getNotifier(this.tasks).notify

以下是完整版

<polymer-element name="todo-list">
<template>

  <h1>You have {{ tasks.length }} tasks, {{ remaining }} remaining</h1>

  <button
    style="border: solid 1px red; padding: 20px; border-radius: 20px; display: inline-block;"
    on-click="{{ doTask }}"
    >
    Click to mark as done
  </button>

  <ul>
    <template repeat="{{ task in othertasks }}">
      <li>{{task}}</li>
    </template>
  </ul>

  <div>
    {{ tasks[0].done }}
  </div>
</template>
<script>
  Polymer({
    tasks: [
      {name: "foo", done: false},
      {name: "bar", done: true}
    ],
    othertasks: ["foo","bar"],
    ready: function () {
      Object.observe(this.tasks, function(change) {           
        console.log(change); // What change
      });
    },
    get remaining() {
      return this.tasks.filter(function(t) { return !t.done }).length;
    },
    doTask: function () {
      this.tasks[0].done = true;
      Object.getNotifier(this.tasks).notify({
        type: 'update',
        name: '0'
      });
    }
  });
</script>
</polymer-element>

我更难理解没有模板的原因。如果我们保持好听众,我们会看到它不是通话,但剩下的就是更新......

答案 2 :(得分:0)

您没有更新观察到的&#34;任务&#34;您的changeState方法中的数组。将其更改为:

changeState: function(e) {
 this.tasks[e.target.getAttribute('data-task-id')].done = !this.tasks[e.target.getAttribute('data-task-id')].done;
}

基本上,您通过重新分配数组的值来创建局部变量 - 除非您明确设置它,否则没有后向引用。现在上面的代码很长,所以你也可以这样做:

changeState: function(e) {
 var _task = this.tasks[e.target.getAttribute('data-task-id')];
 _task.done = !_task.done;

 // re assign
 this.tasks[e.target.getAttribute('data-task-id')] = _task;e
}