0

假设我想用 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),则什么也不会发生。这就是我问题的根源...

4

3 回答 3

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>

当我单击按钮时,标签的值会发生变化,即remininggetter 检测到变化。我已经在 Linux 上的 Chromium 41 和 Firefox 中进行了测试。

您可以在http://embed.plnkr.co/HXaKsQHjchqwe0P3bjy5/preview上测试我的代码

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

编辑

在通过 Twitter 与@romaintaz 交谈后,似乎只有当按钮位于 if 模板中时才会出现问题,如下所示:

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

在这种情况下,removinggetter 不再检测列表项属性之一的更改。

目前我只有一个快速的'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>

戳这里: http ://embed.plnkr.co/YgqtKgYRaRTZmoKEFwBy/preview

于 2015-03-23T21:08:09.623 回答
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 的hack 起作用的原因。他替换了对象。另一种解决方案是使用手动通知更改

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>

我很难理解为什么它在没有模板的情况下工作。如果我们让我们的听众保持良好,我们会看到它不是调用但剩余的是更新......

于 2015-03-25T15:47:12.107 回答
0

您没有在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
}
于 2015-03-20T20:21:34.643 回答