代码之家  ›  专栏  ›  技术社区  ›  Dom

创建自定义的淘汰绑定,动态地从筛选的可观察数组添加绑定元素

  •  1
  • Dom  · 技术社区  · 8 年前

    我正在尝试创建一个定制的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 元素并正确更新它们。问题是我想添加 <ul> 之后 我使用此绑定的节点,而不是它内部的节点。当我改变的时候 jQuery(element).append(ul_element); jQuery(element).after(ul_element); 它不起作用,不显示任何内容。

    另外,我不确定在我的自定义绑定中直接向绑定上下文添加可观察的内容是否是“正确”的事情。

    2 回复  |  直到 8 年前
        1
  •  2
  •   user3297291    8 年前

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

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

    template foreach 绑定,带有一小部分自定义行为。就个人而言,我将把自定义绑定重写到组件中。例如:

    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>

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


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

        2
  •  0
  •   Dom    8 年前

    我有一个可行的解决方案,但我觉得我在使用淘汰的方式并不是有意的或完全错误的。我不会接受我自己的答案,因为我希望其他人指出这个解决方案中不可避免的陷阱。

    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 我很惊讶没有它的工作。第二,我没有使用任何淘汰赛 custom disposal logic 我担心这也是一个错误。

    推荐文章