Notebook Twitter Bootstrap Popover Binding

I am trying to create a custom binding for twitter tooltips that reference a template, but I am having problems with the connecting part of the content inside the popover after creating it.

I already asked this question, but I feel that they are mostly pretty dirty, and I'm pretty close to a reusable solution that uses templates the way I want.

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

Updated code based on the answer below that allows you to do this without additional toProperties binding

 // 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" } }; 
+6
source share
4 answers

You need to use my old friend 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 }; } }; 

Then you need to add the data binding attribute to the html you are creating:

  var tmplDom = $('<div/>', { "class": "ko-popover", "id": domId, "data-bind": "withProperties: { label: '" + viewModel.label() + "', required: '" + viewModel.required() + "' }" 

I compiled jsFiddle showing this. There were several fixes, I had to copy the popover options for each popover, otherwise they all ended up with the last set of values.

  var popoverOptions = $.extend({}, ko.bindingHandlers.popover.options); popoverOptions.content = options.content; 

And I also had to apply the snap to the popup only if it is visible, otherwise it tries to try to snap the whole page.

 $(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]); } }); 

It also looks like a 2-way way, since you can change the values ​​in the pop-up window and update elements without pop-ups, but I will not lie, I did not expect this to happen!

+6
source

Here's another version of the Popock popock binding that uses the html template defined in the document.

Check out this script: https://jsfiddle.net/2cpcgz3o/

 (function () { var templateEngine = new ko.nativeTemplateEngine(); ko.bindingHandlers.customPopover = { init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { var placement = allBindings.get("placement") || "top", trigger = allBindings.get("trigger") || "click", templateName = allBindings.get("customPopover") || null, $element = $(element); $element.popover({ placement: placement, trigger: trigger, html: true, content: "&nbsp;" }); $element.on("inserted.bs.popover", function () { var container = $element.next().find(".popover-content")[0]; if (templateName) { ko.renderTemplate(templateName, viewModel, { templateEngine: templateEngine }, container); } else { container.innerHTML = $element.attr("data-content"); } }); ko.utils.domNodeDisposal.addDisposeCallback(element, function () { $element.popover("destroy"); }); } }; })(); var model = { linkText: "Click me!", innerText: "Some fancy text" }; ko.applyBindings(model); 
 <link href="https://cdn.jsdelivr.net/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <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.2.0/knockout-min.js"></script> <a data-bind="text: linkText, customPopover: 'popover-template', trigger: 'focus', placement: 'bottom'" tabindex="0" role="button"></a> <script type="text/html" id="popover-template"> <span data-bind="text: innerText"></span> </script> 
+2
source

I applied a different answer here: fooobar.com/questions/218164 / ...

This works much better for me, especially for a simple popover.

 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" } }; 

Then binding:

 <span data-bind="popover: { content: mySimpleTextContent }"></span> 

You can override other parameters.

+1
source

Slightly modified by dodbrian . Content is associated with observable.

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); 
 body { padding: 20px; } 
 <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> 
0
source

All Articles