我正在尝试为引用模板的twitter boostrap popover创建一个自定义绑定,但是一旦创建了弹出窗口内容的绑定部分我就遇到了问题。
我之前已经看到过这个问题,但我觉得它们大部分都非常混乱,而且我非常接近使用模板的可重用解决方案。
http://jsfiddle.net/billpull/Edptd/
// Bind Twitter Popover
ko.bindingHandlers.popover = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var tmplId = ko.utils.unwrapObservable(valueAccessor());
var tmplHtml = $('#' + tmplId).html();
var uuid = guid();
var domId = "ko-bs-popover-" + uuid;
var tmplDom = $('<div/>', {
"class" : "ko-popover",
"id" : domId
}).html(tmplHtml);
options = {
content: tmplDom[0].outerHTML
};
var popoverOptions = ko.utils.extend(ko.bindingHandlers.popover.options, options);
console.log($(element));
console.log(element);
$(element).bind('click', function () {
$(this).popover(popoverOptions).popover('toggle');
ko.applyBindings(bindingContext, document.getElementById(domId));
});
},
options: {
placement: "right",
title: "",
html: true,
content: "",
trigger: "manual"
}
};
=== EDIT
根据以下答案更新了代码,允许您在没有额外的withProperties绑定的情况下执行此操作
// Bind Twitter Popover
ko.bindingHandlers.popover = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// read popover options
var popoverBindingValues = ko.utils.unwrapObservable(valueAccessor());
// set popover template id
var tmplId = popoverBindingValues.template;
// set popover trigger
var trigger = popoverBindingValues.trigger;
// get template html
var tmplHtml = $('#' + tmplId).html();
// create unique identifier to bind to
var uuid = guid();
var domId = "ko-bs-popover-" + uuid;
// create correct binding context
var childBindingContext = bindingContext.createChildContext(viewModel);
// create DOM object to use for popover content
var tmplDom = $('<div/>', {
"class" : "ko-popover",
"id" : domId
}).html(tmplHtml);
// set content options
options = {
content: tmplDom[0].outerHTML
};
// Need to copy this, otherwise all the popups end up with the value of the last item
var popoverOptions = $.extend({}, ko.bindingHandlers.popover.options);
popoverOptions.content = options.content;
// bind popover to element click
$(element).bind(trigger, function () {
$(this).popover(popoverOptions).popover('toggle');
// if the popover is visible bind the view model to our dom ID
if($('#' + domId).is(':visible')){
ko.applyBindingsToDescendants(childBindingContext, $('#' + domId)[0]);
}
});
return { controlsDescendantBindings: true };
},
options: {
placement: "right",
title: "",
html: true,
content: "",
trigger: "manual"
}
};
答案 0 :(得分:6)
您需要使用我的老朋友custom bindings。
ko.bindingHandlers.withProperties = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// Make a modified binding context, with a extra properties, and apply it to descendant elements
var newProperties = valueAccessor(),
innerBindingContext = bindingContext.extend(newProperties);
ko.applyBindingsToDescendants(innerBindingContext, element);
// Also tell KO *not* to bind the descendants itself, otherwise they will be bound twice
return { controlsDescendantBindings: true };
}
};
然后,您需要将data-bind属性添加到您正在生成的html中:
var tmplDom = $('<div/>', {
"class": "ko-popover",
"id": domId,
"data-bind": "withProperties: { label: '" + viewModel.label() + "', required: '" + viewModel.required() + "' }"
我把jsFiddle放在一起展示了这一点。有几个问题,我不得不为每个popover复制popover选项,否则他们都会得到最后一组值。
var popoverOptions = $.extend({}, ko.bindingHandlers.popover.options);
popoverOptions.content = options.content;
如果可见,我还必须将绑定应用于弹出 ,否则它似乎会尝试绑定到整个页面。
$(element).bind('click', function () {
$(this).popover(popoverOptions).popover('toggle');
// If you apply this when the popup isn't visible, I think that it tries to bind to thewhole pageand throws an error
if($('#' + domId).is(':visible'))
{
ko.applyBindings(viewModel, $('#' + domId)[0]);
}
});
这似乎也是双向的,因为你可以更改弹出窗口中的值并更新非弹出元素,但我不会说谎,我没想到会发生这种情况!
答案 1 :(得分:2)
答案 2 :(得分:1)
我在这里改编了另一个答案:https://stackoverflow.com/a/16876013/1061602
这对我来说效果更好,特别是对于简单的弹出窗口。
ko.bindingHandlers.popover = {
init: function (element, valueAccessor) {
var local = ko.utils.unwrapObservable(valueAccessor()),
options = {};
ko.utils.extend(options, ko.bindingHandlers.popover.options);
ko.utils.extend(options, local);
$(element).popover(options);
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).popover("destroy");
});
},
options: {
placement: "top"
}
};
然后绑定是:
<span data-bind="popover: { content: mySimpleTextContent }"></span>
您可以明显覆盖其他选项。
答案 3 :(得分:0)
略微修改dodbrian的例子。内容绑定到一个可观察的。
https://jsfiddle.net/SergeyZhilyakov/0zxamcj6/14/
var model = {
linkText: "Hover me!",
innerText: ko.observable("Please, wait...")
};
ko.bindingHandlers.popover = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var $element = $(element);
var placement = allBindings.get("placement") || "top";
var trigger = allBindings.get("trigger") || "hover";
var content = allBindings.get("popover");
$element.popover({
placement: placement,
trigger: trigger,
content: content()
});
var popover = $element.data("bs.popover");
content.subscribe(function(newValue) {
popover.options.content = newValue;
popover.setContent();
popover.$tip.addClass(popover.options.placement);
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$element.popover("destroy");
});
}
};
ko.applyBindings(model);
setTimeout(function() {
model.innerText("Done!");
}, 3000);
&#13;
body {
padding: 20px;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<button type="button" class="btn btn-default" data-bind="text: linkText, popover: innerText, placement: 'bottom'">Comment</button>
&#13;