2012-02-10 131 views
23

假設我有knockout.js模板是這樣的:唯一ID

<script type="text/html" id="mytemplate"> 
    <label for="inputId">Label for input</label> 
    <input type="text" id="inputId" data-bind="value: inputValue"/> 
</script> 

如果我呈現我最終具有相同id多個輸入頁面上的幾個地方這個模板(和與值相同的幾個標籤具有相同的),這具有不良後果。尤其是,所有依賴於ID的代碼可能無法正常工作(在我的例子中,我使用了jquery.infieldlabel插件,它被多個輸入與相同的ID混淆)。我現在解決這個問題的辦法是我加的獨特id屬性,我綁定到模板的模型:

<script type="text/html" id="mytemplate"> 
    <label data-bind="attr: {for: id}>Label for input</label> 
    <input type="text" data-bind="attr: {id: id}, value: inputValue"/> 
</script> 

這工作,但因爲我要在我的模型這種人工id屬性這不是很優雅這不是用於其他任何事情。我想知道這裏是否有更好的解決方案。

回答

61

不依賴於字段綁定順序的替代方法是使綁定對數據本身設置id屬性,該屬性需要是可觀察的。

ko.bindingHandlers.uniqueId = { 
    init: function(element, valueAccessor) { 
     var value = valueAccessor(); 
     value.id = value.id || ko.bindingHandlers.uniqueId.prefix + (++ko.bindingHandlers.uniqueId.counter); 

     element.id = value.id; 
    }, 
    counter: 0, 
    prefix: "unique" 
}; 

ko.bindingHandlers.uniqueFor = { 
    init: function(element, valueAccessor) { 
     var value = valueAccessor(); 
     value.id = value.id || ko.bindingHandlers.uniqueId.prefix + (++ko.bindingHandlers.uniqueId.counter); 

     element.setAttribute("for", value.id); 
    } 
}; 

你會用它喜歡:

<ul data-bind="foreach: items"> 
    <li> 
     <label data-bind="uniqueFor: name">Before</label> 
     <input data-bind="uniqueId: name, value: name" /> 
     <label data-bind="uniqueFor: name">After</label> 
    </li> 
</ul> 

樣品:http://jsfiddle.net/rniemeyer/JjBhY/

增加一個屬性,以可觀察到的功能的好處是,當你把它變成JSON傳送回服務器,那麼它就會自然消失,因爲可觀察值將變成它的解包值。

+4

我第二。但是,我是否會感到惱火,並且要求就一個稍微複雜一些的問題尋求建議 - 在那裏你有一對無線電,例如,作爲標籤的「是」和「否」,每一個綁定回單個布爾可觀察值,例如, 「活躍」。此刻,所有4個元素--2個收音機和2個標籤 - 都獲得相同的ID。無線電對本身出現多次,所以我使用Knockout綁定爲每個對生成一個唯一的名稱屬性,所以我需要能夠將當前對的特定名稱前綴到ID。 – 2012-07-16 03:55:24

+0

您也可以使用data-bind =「attr:{for:'status_'+ $ index}」和data-bind =「attr:{id:'status_'+ $ index}」爲唯一ID – viperguynaz 2013-07-26 19:14:00

+0

您也可以使用data-bind =「attr:{for:'status_'+ $ index}」和data-bind =「attr:{id:'status_'+ $ index}」。 $ index引用當前數組項目的從零開始的索引。 $ index是一個可觀察值,只要項目的索引發生更改(例如,如果將項目添加到陣列或從陣列中刪除項目),它就會更新。 – viperguynaz 2013-07-26 19:27:44

6

我做了這樣的事情在過去的:

ko.bindingHandlers.uniqueId = { 
    init: function(element) { 
     element.id = ko.bindingHandlers.uniqueId.prefix + (++ko.bindingHandlers.uniqueId.counter); 
    }, 
    counter: 0, 
    prefix: "unique" 
}; 

ko.bindingHandlers.uniqueFor = { 
    init: function(element, valueAccessor) { 
     var after = ko.bindingHandlers.uniqueId.counter + (ko.utils.unwrapObservable(valueAccessor()) === "after" ? 0 : 1); 
      element.setAttribute("for", ko.bindingHandlers.uniqueId.prefix + after); 
    } 
}; 

你會使用他們喜歡的:

<ul data-bind="foreach: items"> 
    <li> 
     <label data-bind="uniqueFor: 'before'">Before</label> 
     <input data-bind="uniqueId: true, value: name" /> 
     <label data-bind="uniqueFor: 'after'">After</label> 
    </li> 
</ul> 

所以,它只是不斷增加自身的ko.bindingHandlers.uniqueId.counter綁定狀態。然後,uniqueFor綁定只需知道是否在字段之前或之後知道如何獲得正確的id。

樣品在這裏:http://jsfiddle.net/rniemeyer/8KJD3/

如果您的標籤不靠近他們的領域(也許在一個表中的單獨行每個標籤之前勢必多個輸入),那麼你就需要考慮不同的策略。

+0

我在想沿着這些線路的東西太多,而且必定會在我的情況下工作,但我不喜歡這個解決方案是,它取決於標籤呈現的順序。謝謝你的代碼,但它絕對是其中一個選項。 – 2012-02-10 20:07:11

+0

如果訂單是一個問題,那麼這裏是另一種選擇:http://jsfiddle.net/rniemeyer/JjBhY/。這是相似的,但是綁定會對可能觀察到的內容設置「id」屬性。無論綁定是什麼,它首先會更新id。關於在observable函數上設置「id」屬性的好處是,當它變成JSON時它將會消失,因爲您只剩下可觀察值的解包值。 – 2012-02-10 20:23:26

+0

謝謝,這正是我一直在尋找的!你介意發佈這個答案,以便我能接受它嗎? – 2012-02-10 20:50:52

3

我無法回覆所選答案,但是我有一個支持每個輸入值多個唯一ID的代碼的增強版本。這是對我在http://drewp.quickwitretort.com/2012/09/18/0博客並在此重複:

ko.bindingHandlers.uniqueId = { 
    /* 
     data-bind="uniqueId: $data" to stick a new id on $data and 
     use it as the html id of the element. 

     data-which="foo" (optional) adds foo to the id, to separate 
     it from other ids made from this same $data. 
    */ 
    counter: 0, 
    _ensureId: function (value, element) { 

    if (value.id === undefined) { 
     value.id = "elem" + (++ko.bindingHandlers.uniqueId.counter); 
    } 

    var id = value.id, which = element.getAttribute("data-which"); 
    if (which) { 
     id += "-" + which; 
    } 
    return id; 
    }, 
    init: function(element, valueAccessor) { 
     var value = valueAccessor(); 
     element.id = ko.bindingHandlers.uniqueId._ensureId(value, element); 
    }, 
}; 

ko.bindingHandlers.uniqueFor = { 
    /* 
     data-bind="uniqueFor: $data" works like uniqueId above, and 
     adds a for="the-new-id" attr to this element. 

     data-which="foo" (optional) works like it does with uniqueId. 
    */ 
    init: function(element, valueAccessor) { 
     element.setAttribute(
     "for", ko.bindingHandlers.uniqueId._ensureId(valueAccessor(), element)); 
    } 
}; 

現在你可以有多個標記複選框一條記錄自動IDS:

<li data-bind="foreach: channel"> 
    <input type="checkbox" data-which="mute" data-bind="uniqueId: $data, checked: mute">  
    <label data-which="mute" data-bind="uniqueFor: $data">Mute</label> 

    <input type="checkbox" data-which="solo" data-bind="uniqueId: $data, checked: solo">  
    <label data-which="solo" data-bind="uniqueFor: $data">Solo</label> 
</li>