Knockout Twitter Bootstrap Popover Binding

前端 未结 4 1776
礼貌的吻别
礼貌的吻别 2020-12-31 20:38

I am trying to create a custom binding for twitter boostrap popovers that references a template but I am having trouble with the binding part of the content inside of the po

相关标签:
4条回答
  • 2020-12-31 20:46

    I adapted another answer here: https://stackoverflow.com/a/16876013/1061602

    This works a lot 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 the binding is:

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

    You can override other options obviously.

    0 讨论(0)
  • 2020-12-31 20:55

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

    You then need to add a data-bind attribute to the html you are generating:

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

    I've put together a jsFiddle showing this. There were a couple of gotchas, 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 binding to the popup only if it is visible, otherwise it appears to attempt to bind to 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]);
                }
            });
    

    This also appears to be 2-way, in that you can change the values in the popup and it updates the non-popup elements, but I won't lie, I didn't expect that to happen!

    0 讨论(0)
  • 2020-12-31 21:01

    Here's one more version of the Knockout popover binding which makes use of an html template defined within the document.

    Check out this fiddle: 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>

    0 讨论(0)
  • 2020-12-31 21:02

    Slightly modified dodbrian's example. The content is bind to an 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 讨论(0)
提交回复
热议问题