我正在尝试创建一个带有可观察数组的自定义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是“正确”的事情。
答案 0 :(得分:2)
我的一部分想要说:&#34;如果它有效,它可以正常工作,但我也觉得你有点误用自定义绑定......
自定义绑定通常用于执行例如高级用户交互所需的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());
&#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;
如果建议和建议逻辑是&#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,我担心这也是一个错误。