动态生成元素的淘汰数据绑定

Posted

技术标签:

【中文标题】动态生成元素的淘汰数据绑定【英文标题】:knockout data-bind on dynamically generated elements 【发布时间】:2012-06-19 11:19:01 【问题描述】:

如何使剔除数据绑定对动态生成的元素起作用?例如,我在 div 中插入了一个简单的 html 选择菜单,并希望使用敲除选项绑定来填充选项。这是我的代码的样子:

$('#menu').html('<select name="list" data-bind="options: listItems"></select>');

但是这种方法不起作用。有什么想法吗?

【问题讨论】:

在你完成你的 ko.applyBindings(yourVMHere); 之后添加这个; 放弃对这个动态添加的 DOM 元素进行(自动)KO 绑定的想法并手动处理。 底部附近的正确答案:***.com/a/29903552/3093731 【参考方案1】:

如果您在绑定视图模型后动态添加此元素,它将不在视图模型中并且不会更新。你可以做两件事之一。

    将元素添加到 DOM 并通过再次调用 ko.applyBindings(); 重新绑定它 或者从头开始将列表添加到 DOM,并将视图模型中的选项集合留空。在您稍后将元素添加到选项中之前,Knockout 不会呈现它。

【讨论】:

如果我再次调用 applyBindings 会抛出一个错误:错误:您不能将绑定多次应用于同一个元素。 这一定是最新框架的新特性。第二种选择仍然可行,老实说,这是一个更好的选择。 是的,我想这么多,在一个元素上应用绑定一次以上是不好的做法。因为它会触发 2 次,我认为这就是为什么他们在 KO 3 中添加了一些警告【参考方案2】:

淘汰赛 3.3

ko.bindingHandlers.htmlWithBinding = 
          'init': function() 
            return  'controlsDescendantBindings': true ;
          ,
          'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) 
              element.innerHTML = valueAccessor();
              ko.applyBindingsToDescendants(bindingContext, element);
          
    ;

上面的代码 sn-p 允许您使用“htmlWithBinding”属性动态注入 html 元素。然后还会评估添加的子元素...即它们的数据绑定属性。

【讨论】:

【参考方案3】:

重写html绑定代码或创建一个新的。因为 html 绑定可以防止动态 html 中的“注入绑定”:

ko.bindingHandlers['html'] = 
  //'init': function() 
  //  return  'controlsDescendantBindings': true ; // this line prevents parse "injected binding"
  //,
  'update': function (element, valueAccessor) 
    // setHtml will unwrap the value if needed
    ko.utils.setHtml(element, valueAccessor());
  
;

【讨论】:

在查看了该线程上的所有答案和 cmets 之后,IMO 这实际上是“动态生成元素上的淘汰数据绑定”问题的最佳解决方案。很好的解决方案! 如果理解这一点的人可以将其编辑为更易于理解,我将不胜感激:)【参考方案4】:

对于 v3.4.0,使用下面的自定义绑定:

ko.bindingHandlers['dynamicHtml'] = 
    'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) 
        // setHtml will unwrap the value if needed
        ko.utils.setHtml(element, valueAccessor());
        ko.applyBindingsToDescendants(bindingContext, element);
    
;

【讨论】:

当我创建带有绑定的新标签时如何调用这个绑定?【参考方案5】:

编辑:自LosManos指出的 IIRC 版本 2.3 以来,这似乎不起作用

您可以使用 myViewModel[newObservable] = ko.observable('') 将另一个 observable 添加到您的视图模型中

之后,再次调用 ko.applyBindings。

这是一个简单的页面,我在其中动态添加段落,新的视图模型和绑定完美无缺。

// myViewModel starts only with one observable
    	var myViewModel = 
    	    paragraph0: ko.observable('First')
    	;
    
    	var count = 0;
    
    	$(document).ready(function() 
    		ko.applyBindings(myViewModel);
    
    		$('#add').click(function() 
    			// Add a new paragraph and make the binding
    			addParagraph();
    			// Re-apply!
    			ko.applyBindings(myViewModel);			
    			return false;	
    		);
    	);
    
    	function addParagraph() 
    		count++;
    		var newObservableName = 'paragraph' + count;
    	    $('<p data-bind="text: ' + newObservableName + '"></p>').appendTo('#placeholder');
    		
    	    // Here is where the magic happens
    		myViewModel[newObservableName] = ko.observable('');
    		myViewModel[newObservableName](Math.random());
    
    		// You can also test it in the console typing
    		// myViewModel.paragraphXXX('a random text')
    	
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>

<div id="placeholder">
    <p data-bind="text: paragraph0"></p>
</div>
    
<a id="add" href="#">Add paragraph</a>

【讨论】:

这似乎引发了相同的“无法应用绑定两次”错误。是否可以告诉 ko.applyBindings() 您要添加到绑定中的非常具体的元素? 此解决方案无法解决问题。通常,动态添加的元素将属于同一类型,这意味着它应该调用与绑定到 VM 相同的处理程序。例如:VM.loadData = f() handling.. 并希望将其数据绑定到任何(特定)div 上,并动态添加到 DOM 中。使用您的方法,新元素如何在单击时具有相同的功能? 检查这个小提琴:jsfiddle.net/rniemeyer/F7pLN 从 2.3 IIRC 版本开始,两次调用 applyBindings 是错误的。【参考方案6】:

这是一个老问题,但这是我希望得到的最新答案(淘汰赛 3.3.0):

当使用 knockout 模板或自定义组件将元素添加到预绑定的 observable 集合时,knockout 将自动绑定所有内容。您的示例看起来像是一个可观察的菜单项集合,可以开箱即用。

【讨论】:

正确答案。只需将元素加载到可淘汰的可观察数组中,而不是直接添加到页面中。这就是淘汰赛的目的。或者,在将动态元素添加到页面之前不要绑定到它。【参考方案7】:

基于this existing answer,我已经实现了与您最初的意图相似的东西:

function extendBinding(ko, container, viewModel) 
    ko.applyBindings(viewModel, container.children()[container.children().length - 1]);


function yourBindingFunction() 
    var container = $("#menu");
    var inner = $("<select name='list' data-bind='options: listItems'></select>");
    container.empty().append(inner);


    extendBinding(ko, container, 
        listItems: ["item1", "item2", "item3"]
    );

这里有一个JSFiddle 可以玩。

请注意,一旦新元素成为 dom 的一部分,就无法通过调用 ko.applyBindings 重新绑定它——这就是我使用 container.empty() 的原因。如果您需要保留新元素并使其随着视图模型的变化而变化,请将 observable 传递给 extendBinding 方法的 viewModel 参数。

【讨论】:

【参考方案8】:

查看此答案:How do define a custom knockout 'options binding' with predefined Text and Value options

ko.applyBindingsToNode 特别有用。

【讨论】:

以上是关于动态生成元素的淘汰数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

js为动态生成/添加的元素绑定事件

JQuery 动态加载 HTML 元素时绑定点击事件无效问题

如何将jquery动态生成的数据绑定事件

在js中怎么给动态生成的元素绑定事件

jquery on()绑定的点击事件在js动态新添加的元素上无效

淘汰组件(不)绑定到新内容