2013-02-11 17 views
6

Próbuję utworzyć niestandardowe powiązanie dla poppers twitter boost, które odwołuje się do szablonu, ale mam problem z wiążącą częścią zawartości wewnątrz popover, gdy został utworzony.Knockout Twitter Bootstrap Popover Binding

Widziałem to pytanie wcześniej, ale czuję, że były one w większości dość nieładne i jestem całkiem blisko rozwiązania wielokrotnego użytku, które używa szablonów, jak chcę.

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

Zaktualizowany kod na podstawie odpowiedzi poniżej, które pozwala to zrobić bez dodatkowych withProperties wiążących

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

Co właściwie problem? Nie opisałeś problemu. –

+0

"Mam problem z wiążącą częścią zawartości wewnątrz popover, gdy została ona utworzona." – BillPull

+0

Jestem pewien, że to musi być możliwe, choć nie jest to proste, ale nie mam czasu, by na to spojrzeć. Dziś wieczorem będę wyglądać. –

Odpowiedz

6

Trzeba użyć mój stary przyjaciel, 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 }; 
    } 
}; 

Następnie należy dodać atrybut danych wiążą się z html są generowania:

Mam ułożyła jsFiddle pokazując tym. Było kilka gotch, musiałem skopiować opcje popover dla każdego popover, w przeciwnym razie wszystkie zakończyły się ostatnim zestawem wartości.

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

I miałem też zastosować wiązanie popup tylko jeśli jest widoczna, w przeciwnym razie wydaje się próbować wiązać się całej strony.

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

To również wydaje się być 2-drożny, w których można zmienić wartości w popup i aktualizuje elementy non-popup, ale nie będę kłamać, nie spodziewałem się, że się wydarzy !

1

I dostosowany inną odpowiedź tutaj: https://stackoverflow.com/a/16876013/1061602

to działa dużo lepiej dla mnie, szczególnie dla prostego 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" 
    } 
}; 

Następnie wiążąca jest:

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

Można zastąpić inne opcje oczywiście.

+1

Nice. Ponieważ opublikowałem to pytanie, stworzyłem bibliotekę rozszerzeń bootstrap-knockout, szukam wszelkich opinii dostępnych https://github.com/billpull/knockout-bootstrap – BillPull

2

Oto jeszcze jedna wersja wiązania poppo knockout, która korzysta z szablonu html zdefiniowanego w dokumencie.

Sprawdź ten skrzypce: 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

nieco zmodyfikowany dodbrian „s przykład. Treść jest powiązana z obserwowalną.

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>

Powiązane problemy