7

テンプレートを参照する twitter ブーストラップ ポップオーバーのカスタム バインディングを作成しようとしていますが、作成されたポップオーバー内のコンテンツのバインディング部分に問題があります。

この質問が以前に尋ねられたのを見たことがありますが、ほとんどがかなり面倒で、テンプレートを好きなように使用する再利用可能なソリューションにかなり近づいています。

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

4 に答える 4

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

次に、生成する 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]);
            }
        });

これも、ポップアップの値を変更でき、ポップアップ以外の要素を更新できるという点で双方向のように見えますが、嘘は言いません。

于 2013-02-12T16:26:01.887 に答える
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>

明らかに他のオプションをオーバーライドできます。

于 2014-01-06T16:14:47.963 に答える
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);
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>

于 2017-04-12T12:18:16.333 に答える