简体   繁体   English

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

[英]How to detect change in a property of an object in an array with Polymer?

Let's say I want to create a todo list with Polymer (v0.5.5). 假设我要使用Polymer(v0.5.5)创建待办事项列表。 In my element, I define a property tasks , which is an array containing a list of objects like { name: 'foo', done: false } . 在我的元素中,我定义了一个属性tasks ,它是一个包含对象列表的数组,例如{ name: 'foo', done: false } I want to display the number of remaining tasks, so I need to detect when the property done of an object included in the array is changed. 我想显示剩余任务的数量,因此我需要检测何时更改数组中包含的对象的done属性。

Here is an extract of the code: 以下是代码摘录:

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

With Firefox, it is working but not with Chrome (41.x). 使用Firefox可以正常运行,但不能使用Chrome(41.x)。 Indeed, Chrome only detect the change of the array itself (for example, if I add a new task, the remaining count is updated correctly). 确实,Chrome仅检测阵列本身的变化(例如,如果添加新任务,则剩余计数将正确更新)。

How do I do that? 我怎么做?

Thanks 谢谢


Edit, regarding Andy answer 编辑,关于安迪的答案

When I do that kind of thing: 当我做这种事情时:

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

and if I do a modification in the array itself (like in tasks.push({...}) ), then a popup is displayed. 如果我对数组本身进行了修改(例如在tasks.push({...}) ),则会显示一个弹出窗口。 But if I change a property of an object contained in the array (eg tasks[0].done = true ), then nothing happen. 但是,如果我更改数组中包含的对象的属性(例如, tasks[0].done = true ),则什么都不会发生。 That's the source of my problem... 那就是我问题的根源...

I'm afraid I don't understand the problem, Romain. 恐怕我不明白这个问题,罗曼。

I've tested with the following code: 我已经使用以下代码进行了测试:

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

When I click on the button, the value of the label changes, ie the remining getter detects the change. 当我单击按钮时,标签的值会更改,即remining吸气剂会检测到更改。 I've tested in Chromium 41 and Firefox on Linux. 我已经在Chromium 41和Linux上的Firefox中进行了测试。

You can test my code on http://embed.plnkr.co/HXaKsQHjchqwe0P3bjy5/preview 您可以在http://embed.plnkr.co/HXaKsQHjchqwe0P3bjy5/preview中测试我的代码

Could you please give me more info of what do you want to do and how does it differ from my code? 您能否给我更多您想做什么的信息,它与我的代码有何不同?

Edit 编辑

After speaking with @romaintaz via Twitter, it seems that the problem only happens when the buttons are inside if templates, like this: 通过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>

In this case, the removing getter doesn't detect the changes of one of the list's item properties anymore. 在这种情况下,可removing吸气剂不再检测到列表项属性之一的更改。

For the moment I only have a quick'n'dirty solution: instead of changing only one property of a list item, change the whole list item, then the getter see it. 目前我只有一个quick'n'dirty解决方案:不是改变列表项只有一个属性,改变了整个名单的项目,然后吸气看到它。

Example: 例:

<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 here: http://embed.plnkr.co/YgqtKgYRaRTZmoKEFwBy/preview 此处的Plunkr: http ://embed.plnkr.co/YgqtKgYRaRTZmoKEFwBy/preview

Just my 2 cents 就是我的2美分

In fact the problem is independent of the presence of the button in a template. 实际上,问题与模板中按钮的存在无关。 You just have to use a repeat template on a list to reproduce the problem. 您只需要在列表上使用重复模板即可重现该问题。 The code below demonstrates it. 下面的代码对此进行了演示。

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

I actually find it rather natural. 我实际上发现它很自然。 When we look at the specification Object.observe (). 当我们查看规范Object.observe()时。 Indeed on an Array, it is triggered only if we: 实际上,在数组上,只有在以下情况下才触发它:

  • Delete an element 删除元素
  • Add an element 添加元素
  • Modify an item (here we speak of reference change) 修改项目(此处称为参考更改)

So it will not fire if you change an internal property of an element in the array. 因此,如果您更改数组中元素的内部属性 ,则不会触发 If we add a listener, in a ready method , we will see that it is not triggered by our doTask method. 如果在ready方法中添加侦听器,我们将看到它不是由doTask方法触发的。 And this why that the Horacio 's hack works . 这就是为什么Horacio的hack起作用的原因。 He replace the object. 他替换了对象。 Another solution is to manually notify the change using 另一种解决方案是使用手动通知更改

Object.getNotifier(this.tasks).notify

Below is a full version 以下是完整版本

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

I have more trouble understanding why it works without the template. 我在理解为什么没有模板的情况下会遇到更多麻烦。 If we keep our listener well we see that it is not call but remaining is updating ... 如果我们让听众保持良好状态,我们会发现它不是呼叫,而是剩余正在更新...

You are not updating the observed "task" array in your changeState method. 您没有在changeState方法中更新观察到的“任务”数组。 Change it to this: 更改为此:

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

Basically you are creating a local variable by reassigning the value of the array - there is no back reference until you explicit set it. 基本上,您是通过重新分配数组的值来创建局部变量的-在您明确设置它之前,没有反向引用。 Now the above code is pretty long, so you could also do this: 现在上面的代码很长,因此您也可以这样做:

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM