【问题标题】:Knockout Twitter Bootstrap Popover Binding淘汰赛 Twitter Bootstrap 弹出框绑定
【发布时间】:2013-02-11 22:21:12
【问题描述】:

我正在尝试为引用模板的 twitter boostrap 弹出框创建自定义绑定,但在创建后弹出框内的内容绑定部分出现问题。

我之前看到过这个问题,但我觉得它们大多都很混乱,而且我非常接近一个可重用的解决方案,它可以按照我的意愿使用模板。

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

===编辑

根据以下答案更新代码,无需额外的 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"
    }
};

【问题讨论】:

  • 这里的实际问题是什么?你还没有描述问题是什么。
  • "创建后弹出框内的内容绑定部分出现问题。"
  • 我确信这一定是可能的,虽然不是直截了当,但我现在没有时间看。我今晚去看看。
  • 谢谢,我相信我只是在应用绑定时没有访问正确的变量,当您在页面上有多个视图模型时,这似乎更加棘手。
  • @BillPull 我已经玩了几个小时你的库淘汰赛-boostrap 试图在弹出窗口中添加一个选择,在它呈现后我想使它成为一个引导选择控件。如何在您的弹出框绑定中实现 afterRender? silviomoreto.github.io/bootstrap-select/3

标签: twitter-bootstrap knockout.js popover knockout-2.0


【解决方案1】:

你需要使用我的老朋友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 };
    }
};

然后你需要为你正在生成的 html 添加一个 data-bind 属性:

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

我已经整理了一个 jsFiddle 来展示这个。有几个陷阱,我必须为每个弹出框复制弹出框选项,否则它们都会以最后一组值结束。

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

这似乎也是双向的,因为您可以更改弹出窗口中的值并更新非弹出元素,但我不会撒谎,我没想到会发生这种情况!

【讨论】:

    【解决方案2】:

    这是 Knockout 弹出框绑定的另一个版本,它使用文档中定义的 html 模板。

    看看这个小提琴: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>

    【讨论】:

    • ko.renderTemplate 在升级到 bootstrap 到 3.4.1 后修复了我的问题。谢谢
    【解决方案3】:

    我在这里改编了另一个答案: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>
    

    您显然可以覆盖其他选项。

    【讨论】:

    【解决方案4】:

    稍微修改了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);
    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>

    【讨论】:

      猜你喜欢
      • 2016-07-03
      • 2016-11-15
      • 2018-09-05
      • 2013-02-04
      • 1970-01-01
      • 2013-10-31
      • 2013-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多