【问题标题】:Create custom Knockout binding that dynamically adds bound elements from filtered observable array创建自定义 Knockout 绑定,从过滤后的可观察数组中动态添加绑定元素
【发布时间】:2018-11-06 23:00:30
【问题描述】:

我正在尝试创建一个自定义 KO 绑定,它接受一个可观察数组,并向 DOM 添加一个嵌套元素以包含可观察数组中元素的过滤子集。

在初始化我的自定义绑定时,我认为我需要做两件事。首先扩展绑定上下文,添加第二个 observable 数组来保存 this 绑定到的 observable 数组的过滤子集。其次,在绑定元素之后添加我想要的DOM元素。

然后在更新此绑定绑定到的可观察数组时,填充在初始化期间添加到绑定上下文的可观察数组。

到目前为止,我有以下不工作的、大大简化的实验。

ko.bindingHandlers.suggester = {
    init: function ( element, valueAccessor, allBindings, viewModel, bindingContext ) {
        var innerBindingContext = bindingContext.extend(valueAccessor);
        innerBindingContext.suggestions = ko.observableArray();
        var ul_element = jQuery(
            '<ul data-bind="foreach: suggestions">' +
            '<li data-bind="text: suggestionText"></li>' +
            '</ul>'
        );
        jQuery(element).after(ul_element);
        ko.applyBindingsToDescendants(innerBindingContext, element);
        return { controlsDescendantBindings: true };
    },
    update: function ( element, valueAccessor, allBindings, viewModel, bindingContext ) {
        var self = this;

        jQuery.each(ko.unwrap(valueAccessor), function (index,value) {
            if (/*do some filtering*/) {
                bindingContext.suggestions.push({suggestionText: value});
            }
        });
    }
};

我很清楚以上是非常错误的,但我从一个非常错误的想法跳到下一个,真的需要一些帮助。

======== 编辑 ========

我一直在玩,我有一些接近我所追求的东西,但仍然不起作用。

ko.bindingHandlers.autocomplete = {
    init: function ( element, valueAccessor, allBindings, viewModel, bindingContext ) {

        bindingContext.suggestions = ko.observableArray([{suggestionText: 'fred'}]);

        var ul_element = jQuery(
            '<ul data-bind="foreach: suggestions">' +
            '<li data-bind="text: suggestionText"></li>' +
            '</ul>'
        );

        jQuery(element).append(ul_element);

    },
    update: function ( element, valueAccessor, allBindings, viewModel, bindingContext ) {
        var self = this;
        bindingContext.suggestions.push({suggestionText: "another1"});
        bindingContext.suggestions.push({suggestionText: "another2"});
    }
};

这会将可观察数组 suggestions 添加到绑定上下文,将 ul/li 元素添加到 DOM 并正确更新它们。问题是我想在我使用此绑定的节点之后添加&lt;ul&gt; ,而不是在其中。当我将 jQuery(element).append(ul_element); 更改为 jQuery(element).after(ul_element); 时,它不起作用,没有显示任何内容。

此外,我不确定在我的自定义绑定中直接向绑定上下文添加可观察对象是否是“正确”的做法。

【问题讨论】:

    标签: knockout.js


    【解决方案1】:

    我的一部分想说:“如果它有效,它就有效”,但我也觉得你有点滥用自定义绑定......

    自定义绑定通常用于执行所需的 DOM 修改,例如高级用户交互。对于将 UI 代码与视图模型相结合的更高级的可重用模式,有 knockout components

    您将自定义绑定用作高级templateforeach 绑定,并带有一小部分自定义行为。就个人而言,我会将自定义绑定重写为组件。例如:

    ko.components.register('suggestionWidget', {
      viewModel: function(params) {
        // Component requires two params:
        //  - suggestions: an (observable) array of "things"
        //  - filter: a (wrapped) filter function to go from 
        //      `thing -> bool`
        this.suggestions = ko.pureComputed(
          () => ko.unwrap(params.suggestions)
            .filter(ko.unwrap(params.filter))
        );
      },
      template: `
        <ul data-bind="foreach: suggestions">
          <li data-bind="text: suggestionValue"></li>
        </ul>`
    });
    
    
    const App = function() {
      this.searchValue = ko.observable("");
      this.filter = ko.pureComputed(() =>
        this.searchValue()
          ? fruitSuggestion => fruitSuggestion
              .suggestionValue
              .includes(this.searchValue().toLowerCase())
          : () => false
      );
      
      this.fruitSuggestions = ko.observableArray(
        ["apple", "banana", "orange", "mango", "pineapple"].map(suggestionValue => ({ suggestionValue }))
      );
    }
    
    ko.applyBindings(new App());
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <input type="text" data-bind="textInput: searchValue" placeholder="type 'apple' to get suggestions">
    
    <div data-bind="component: {
                      name: 'suggestionWidget',
                      params: { 
                        filter: filter, 
                        suggestions: fruitSuggestions
                      }
                    }"></div>

    如果建议和建议逻辑“更通用”,您可以将它们烘焙到组件中。我选择让视图模型同时提供过滤器和内容,但这取决于你。


    这个答案完全绕过了您当前的代码,我可以想象您想要更接近这种方法的东西......但是,您要求提供一些见解,所以我想我会以完全不同的观点提出意见:)

    【讨论】:

    • 我认为您的回答没有任何帮助,它真的救了我。谢谢!
    【解决方案2】:

    我有一个可行的解决方案,但感觉好像我以非预期或完全错误的方式使用 Knockout。我不会接受我自己的答案,因为我希望其他人指出此解决方案中不可避免的缺陷。

    ko.bindingHandlers.autocomplete = {
        init: function ( element, valueAccessor, allBindings, viewModel, bindingContext ) {
    
            bindingContext.$data.suggestions = ko.observableArray();
    
            var ul_element = jQuery(
                '<ul>' +
                '<li data-bind="text: suggestionText"></li>' +
                '</ul>'
            );
    
        },
        update: function ( element, valueAccessor, allBindings, viewModel, bindingContext ) {
            var self = this;
            //console.log(valueAccessor());
            jQuery.each(ko.unwrap(valueAccessor()), function (index, suggestion) {
                console.log(suggestion());
                bindingContext.$data.suggestions.push({suggestionText: suggestion()});
            });
    
        }
    };
    

    这在this fiddle中工作

    这个解决方案有两个直接的问题让我担心。首先,我没有在init 中使用return { controlsDescendantBindings: true };,我很惊讶它没有它也能正常工作。其次,我没有使用任何 Knockout 的 custom disposal logic,我担心这也是一个错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-18
      • 2014-05-12
      • 2011-12-20
      • 1970-01-01
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多