2012-02-10 137 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>