0

Polymer (v0.5.5) で todo リストを作成したいとしましょう。私の要素では、プロパティを定義します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

ちょうど私の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 メソッドによってトリガーされないことがわかります。これが、ホラシオのハックが機能する理由です。彼はオブジェクトを置き換えます。別の解決策は、変更を手動で通知することです

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 に答える
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ゲッターが変更を検出します。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 はリストの項目プロパティの 1 つの変更を検出しなくなります。

現時点では、簡単な解決策しかありませんリスト項目の 1 つのプロパティのみを変更するのではなく、リスト項目全体を変更してから、ゲッターがそれを確認します。

例:

<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

于 2015-03-23T21:08:09.623 に答える
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 に答える