1

アイテムの配列を持つノックアウトviewModelがあります。すべてのアイテムで1つのプロパティ(選択済み)を監視し、変更されたときにアクションを実行したいと思います。

そのために私はそれを行うSectionManagerを持っています。マネージャを初期化し、すべてのアイテムのサブスクライブを設定します。myidクロージャはキャプチャされません。常に最後3の値です。誰かが私がそれを失った場所を教えてもらえますか?

例:リスト内のアイテムをクリックすると、アスタリスクはそのアイテムが選択されているかどうかを示します。それはうまくいきます。サブスクライブされた関数も呼び出さmyidれ、コンソールに書き込まれますが、それは常に3です。

ジョン

HTML:

  <!DOCTYPE html>
  <html>
  <head>
     <title></title>
     <script type="text/javascript" src="Scripts/jquery-1.7.2.js"></script>
     <script type="text/javascript" src="Scripts/knockout-2.1.0.debug.js"></script>
     <script type="text/javascript" src="test.js"></script>
  </head>
  <body>
     <ul data-bind="foreach: roles">
        <li data-bind="click: toggle">
           <span data-bind="text: id"></span>
           <span data-bind="visible: selected">*</span>
        </li>
     </ul>
  </body>
  </html>

そしてこのスクリプト:

  var roles = [
     { id: 1 },
     { id: 2, selected: true },
     { id: 3 }
  ];

  var viewModel = (function (roles) {
     var obj = {};
     var arr = [];
     for (var i = 0; i < roles.length; i++) {
        arr.push({
           id: roles[i].id,
           selected: ko.observable(roles[i].selected || false),
           toggle: function () {
              this.selected(!this.selected());
           }
        });
     }
     obj.roles = ko.observable(arr);

     return obj;
  })(roles);

  var sectionManager=(function(){
     return {
        init: function (roles) {
           for (var i = 0; i < roles.length; i++) {
              var item = roles[i];
              var myid = item.id;

              item.selected.subscribe(function () {
                 console.log(myid);  // ALWAYS 3!!
              });
           }
        }
     };
  })();

  $(function () {
     sectionManager.init(viewModel.roles());
     ko.applyBindings(viewModel);
  });
4

1 に答える 1

3

JavaScriptのクロージャが原因で発生する古典的な問題が発生しています。ループには、実際には。という名前の変数が1つだけありますmyid。各アイテムをサブスクライブするときに作成する関数はすべて、同じmyid変数にアクセスできます。ループの終了時の値は3であるため、ハンドラーを実行すると、すべてのハンドラーがその変数の値を報告します。

JavaScriptのクロージャとスコープについては多くのリファレンスがあります。たとえば、ここに別のSOの質問があります: ループ内のJavaScriptクロージャ–簡単な実用的な例

これを処理する1つの方法は、中間変数を使用せず、ハンドラーが現在のアイテムで実行されるようにすることです。

item.selected.subscribe(function () {
    console.log(this.id);
}, item);

この例では、2番目の引数はthis関数が実行されるときの値を定義します。したがって、コンテキストとして正しいアイテムを使用して実行され、からそのプロパティにアクセスできますthis

http://jsfiddle.net/rniemeyer/WV6g5/

item.id2番目の引数でも使用できます。

以下で指摘するように、変数を取り込んでその特定の値の周りにクロージャを作成する関数を返す関数を確実に作成できます。

var subscriber = function(id) 
{ 
    return function() { 
        console.log(id); 
    }; 
}; 

...

item.selected.subscribe(subscriber(myid)); 

thisKnockoutのコンテキストでは、データアイテムを処理し、ハンドラーが実行されるときに確実に処理する方が現実的だと思います。

于 2012-08-16T17:46:41.373 に答える