【问题标题】: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 绑定的想法并手动处理。
  • 底部附近的正确答案:stackoverflow.com/a/29903552/3093731

标签: javascript jquery knockout.js


【解决方案1】:

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

  1. 将元素添加到 DOM 并通过再次调用 ko.applyBindings(); 重新绑定它
  2. 或者从头开始将列表添加到 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 特别有用。

      【讨论】:

        猜你喜欢
        • 2012-10-08
        • 2015-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-09
        • 1970-01-01
        相关资源
        最近更新 更多