2013-02-11 9 views
6

Ich versuche eine benutzerdefinierte Bindung für Twitter Booster Popovers zu erstellen, die auf eine Vorlage verweist, aber ich habe Probleme mit dem verbindlichen Teil des Inhalts innerhalb des Popover, sobald es erstellt wurde.Knockout Twitter Bootstrap Popover Bindung

Ich habe diese Frage schon einmal gesehen, aber ich habe das Gefühl, dass sie meistens ziemlich unordentlich sind und ich bin ziemlich nah an einer wiederverwendbaren Lösung, die Vorlagen benutzt, wie ich will.

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

aktualisiert Code basierend auf Antwort unten, dass man es ohne die zusätzlichen withProperties zu tun erlaubt Bindung

// 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

Was hier eigentlich das Problem? Sie haben nicht beschrieben, was das Problem ist. –

+0

"Ich habe Probleme mit dem verbindlichen Teil des Inhalts innerhalb des Popover, sobald er erstellt wurde." – BillPull

+0

Ich bin sicher, dass es möglich sein muss, obwohl nicht einfach, aber ich habe keine Zeit, um den Moment zu sehen. Ich werde heute Abend nachsehen. –

Antwort

6

Sie müssen meine alten verwenden Freund, 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 }; 
    } 
}; 

Sie benötigen dann eine Daten-bind-Attribut auf die html hinzufügen Sie generieren:

Ich habe eine jsFiddle zeigt diese zusammen. Es gab ein paar Fehler, ich musste die Popover-Optionen für jedes Popover kopieren, sonst hatten alle die letzten Werte.

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

Und ich hatte auch das Popup-Bindung anwenden nur wenn es sichtbar ist, sonst erscheint es auf die ganze Seite zu binden, um zu versuchen.

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

Dies scheint auch 2-Wege zu sein, dass Sie die Werte im Popup ändern können und es aktualisiert die nicht-Popup-Elemente, aber ich werde nicht lügen, ich habe nicht erwartet geschehen !

1

angepasst ich eine andere Antwort hier: https://stackoverflow.com/a/16876013/1061602

Dieses viel besser funktioniert für mich, vor allem für einen einfachen 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" 
    } 
}; 

Dann wird die Bindung ist:

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

Sie können natürlich auch noch andere Optionen außer Kraft setzen.

+1

Schön. Da ich diese Frage gestellt habe, habe ich eine Bibliothek von Bootstrap-Knockout-Erweiterungen gemacht Ich bin auf der Suche nach Feedback verfügbar https://github.com/billpull/knockout-bootstrap – BillPull

2

Hier ist eine weitere Version der Knockout-Popover-Bindung, die eine im Dokument definierte HTML-Vorlage verwendet.

Schauen Sie sich diese Geige: 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

Leicht dodbrian ‚s Beispiel modifiziert. Der Inhalt ist an ein Observables gebunden.

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>