创建自定义Knockout绑定,动态添加已过滤的可观察数组

时间:2018-05-28 11:00:14

标签: knockout.js

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

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

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

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

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并正确更新它们。问题是我想在之后添加{/ 1>} 我正在使用此绑定的节点,而不是在其中。当我将<ul>更改为jQuery(element).append(ul_element);时,它无法正常显示。

此外,我不确定在我的自定义绑定中直接向绑定上下文添加observable是“正确”的事情。

2 个答案:

答案 0 :(得分:2)

我的一部分想要说:&#34;如果它有效,它可以正常工作,但我也觉得你有点误用自定义绑定......

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

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

&#13;
&#13;
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());
&#13;
<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>
&#13;
&#13;
&#13;

如果建议和建议逻辑是&#34;更通用&#34;,您可以将这些逻辑烘焙到组件中。我选择让viewmodel提供过滤器和内容,但它取决于你。

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

答案 1 :(得分:0)

我有一个正在运行的解决方案,但感觉好像我使用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

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