简而言之:我正在寻找与binding preprocessing相当的组件。
我正在尝试封装复杂的绑定,例如
<button data-bind="openModalOnClick: {template: '...', action: '...'}, css: {...}">
delete all the things
</button>
在自定义元素中,例如
<confirmation-button>
delete all the things
</confirmation-button>
为了做到这一点,我希望通过动态添加绑定将行为直接附加到自定义元素。
我知道我可以让我的组件将按钮作为模板插入,但生成的标记
<confirmation-button>
<button data-bind="openModalOnClick: {template: '...', action: '...'}, css: {...}">
delete all the things
</button>
</confirmation-button>
将是多余的。
理想情况下,我可以使用组件注册将所需的绑定动态添加到自定义元素。但是,(ab)使用createViewModel
似乎不起作用:
ko.components.register('confirmation-button', {
viewModel: {
createViewModel: function createViewModel(params, componentInfo) {
var Vm;
$(componentInfo.element).attr('data-bind', 'click: function() { confirm("Are you sure"); }');
Vm = function Vm(params) { };
return new Vm(params);
}
},
template: '<!-- ko template: { nodes: $componentTemplateNodes } --><!-- /ko -->'
});
confirmation-button {
border: 1px solid black;
padding: 1rem;
cursor: pointer;
}
<script src="http://knockoutjs.com/downloads/knockout-3.3.0.js"></script>
<confirmation-button>do stuff</confirmation-button>
是否可以以某种方式向自定义元素本身添加动态绑定?
答案 0 :(得分:2)
我尝试了不同的方法来达到预期的效果,并评估了他们的专业和反对意见。不假装有“答案”,这可能对将来的参考有用。我测试过:
ko.bindingHandlers.component.preprocess
:无权访问:自定义元素,模板,组件&amp;父视图模型。 访问:绑定。 loadTemplate
组件加载器:无法访问:自定义元素。 访问:模板,父母和&amp;组件视图模型(通过模板)ko.bindingProvider.instance.preprocessNode
:无法访问:组件视图模型,模板访问:自定义元素,绑定,父视图模型。 #3
看起来是这三者中最合适的。给出以下代码:
ko.bindingProvider.instance.preprocessNode = function(node) {
// access to current viewmodel
var data = ko.dataFor(node),
// access to all parent viewmodels
context = ko.contextFor(node),
// useful to get current binding values
component = ko.bindingProvider.instance.getBindings(node, context);
if (node.nodeName === 'CUSTOM-BUTTON') { // only do if 'my-custom-element'
// kind of 'raw' string extraction but does the job for demo
var getMsg = node.getAttribute('params').split('msg:')[1],
msg = getMsg.slice(0,getMsg.indexOf(','));
$(node).attr('data-bind','click: function() { confirm('+ msg +'())}');
} else {
return null;
}
}
以下小提琴来测试它:http://jsfiddle.net/kevinvanlierde/7b4n9f9h/4/(在JS的顶部,将选项设置为1以测试#2;以及2(默认)以测试#3)。
(第一个答案)注意:虽然这部分实现了OP所要求的“使容器元素变得有用”,但它在之后附加事件而不是之前模板加载;继续参考。
是的,虽然我已经尝试argueing from a Knockout point of view that it might not be advisable,但这是可能的。鉴于事件绑定实际上只是告诉Knockout“将此函数注册到此事件”的语句,您可以直接通过JS设置click
绑定,如下所示:
function customButton(params, parent) {
var self = this;
this.msg = params.msg;
this.label = params.label;
// this is the same as the click binding
parent.addEventListener('click', function(e) {
alert(self.msg()); alert(e.target.nodeName);
}, false);
}
var myComponent = {
viewModel: { createViewModel: function(params, componentInfo) {
var parent = componentInfo.element;
return new customButton(params, parent);
}},
template: { element: 'custom-button-tmpl' }
}
对于attr
和css
绑定,它稍微复杂一些,但考虑到computed
可观察对象只是每次更新其可观察对象时启动的函数,您可以,例如更改按钮背景我们的VM就像这样:
//prop used as function, so name doesn't matter
this.background = ko.computed(function() {
parent.style.backgroundColor = params.bg();
});
在this fiddle中测试。 (单击自定义元素的填充以查看它是绑定事件的自定义元素;更改颜色以查看“对自定义元素的动态绑定”)
答案 1 :(得分:2)
如果我理解正确的话,你想要在渲染自定义组件时加入钩子,并将行为添加到顶层元素而不是底层元素。
正如RPN在this comment中提到的那样,自定义元素上的生命周期事件没有挂钩(在3.2版本中)。基本上,你(ab)使用createViewModel不起作用的原因是因为在呈现任何元素之前调用该代码。
因此,他对该评论的建议也适用于你。目前,最优雅的方法是在顶级元素上使用自定义绑定。如果你想把它变成通用的,你可以这样做:
<custom-element data-bind="render"></custom-element>
然后在render
自定义数据绑定的init
调用中,您可以获取自定义元素的名称,并查找已注册要应用的任何后期处理。这是一个(粗略的)示例小提琴:http://jsfiddle.net/8r891g6b/,这里是javascript以防万一:
ko.components.register('confirm-button', {
viewModel: function (params) {
params = params || {};
this.text = params.text || '(no text passed in)';
},
template: '<button data-bind="text: text"></button>'
});
ko.bindingHandlers.render = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
ko.bindingHandlers.render[element.tagName.toLowerCase()](element);
}
};
ko.bindingHandlers.render['confirm-button'] = function (element) {
ko.utils.registerEventHandler(element, 'click', function (event) {
if (!confirm('Are you sure?')) {
event.preventDefault();
}
});
};
ko.applyBindings();
顺便说一下,这个例子有点不稳定,因为按钮上的点击事件会先按下按钮,无论确认处理程序如何都会发生。我只是坚持你的榜样,但我希望主要的想法很容易理解。