2012-08-16 49 views
1

我有一個項目數組的knockout viewModel。我想觀看所有項目中的一個屬性(選定),並在更改時採取行動。淘汰賽訂閱功能關閉未捕獲

爲此我有一個可以做到這一點的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); 
    }); 

回答

3

您正在運行到所引起的關閉JavaScript的一個經典問題。在你的循環中,實際上只有一個名爲myid的變量。您在訂閱每個項目時創建的功能都可以訪問相同的myid變量。在循環結束時,它的值是3,所以當處理程序運行時,它們都會報告該變量的值。

JavaScript中有很多關於閉包和範圍界定的參考。例如,下面是另一個SO問題:JavaScript closure inside loops – simple practical example

您處理此問題的一種方法是不使用中間變量並確保您的處理程序與當前項目一起運行。

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

在這個例子中,第二個參數定義了函數運行時的值this。因此,它將以正確的項目作爲上下文運行,並且您可以從this訪問其屬性。

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

你甚至可以在第二個參數中使用item.id

正如你在下面指出的那樣,你當然可以創建一個函數,它接受你的變量並讓它返回一個函數來創建圍繞該特定值的閉包。

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

... 

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

基因敲除的背景下,我認爲這是更實際的處理你的數據項,並確保它是this當你的處理程序運行。

+0

這確實是這個特定情況下的正確答案。感謝你的鏈接,一個更通用的解決方案(不依賴'this'但修復閉包)將使用var subscriber = function(id){return function(){console.log(id); }; }; item.selected.subscribe(訂戶(本身份識別碼));如果您同意,也可以將其添加到您的答案中。 – 2012-08-17 06:02:49

+0

我使用備用解決方案更新了答案。在Knockout的情況下,我仍然建議將其綁定到您的數據項。它更簡單,併爲您提供處理物品多個屬性的靈活性。理解和控制'this'的價值是Knockout中的一個重要概念。謝謝。 – 2012-08-17 13:01:32

+0

我明白,如果它只是在訂閱中需要的可觀察性,那就是它。但是我還需要其他信息(可觀察範圍外),因此需要一個新的範圍。在這種情況下,這個例子過於簡單化了,爲此感到遺憾。 – 2012-08-17 15:00:00