Redacted question and example
I'm trying to get Knockout components to bind after the initial ko.applyBindings();
so I can add custom elements dynamically.
In my original post I referred to loading content by ajax, but my problem occurs when custom elements are added to the DOM using something like jQuery append
.
Here's an example:
$(function() {
// Register a simple widget:
ko.components.register('like-widget', {
template: '<div class="alert alert-info">This is the widget</div>'
});
// Apply bindings
ko.applyBindings();
// Wire up 'add' button:
$('#btnAdd').on('click', function() {
$('#addZone').append("<like-widget></like-widget>");
});
});
<link data-require="bootstrap-css@*" data-semver="3.2.0" rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<body>
Here's a widget, declared inline:
<like-widget></like-widget>
<button id='btnAdd'>Add a new widget to the grey box:</button>
<br/><br/>
<div id='addZone' class="well">
Widgets will be appended here
</div>
<p>When you run this code, the widget custom element is indeed added to the box (see source) but the widget's template is not bound, so nothing appears. How do I get it to bind/appear?</p>
</body>
Original post
I've successfully created my new component, <mynew-widget></mynew-widget>
I've seen the joy of peppering my page with them and everything works beautifully... until I load new content (an AJAX-loaded modal popup, for example) which contains <mynew-widget></mynew-widget>
. Nothing happens.
Is this a limitation of Knockout or have I wired things up incorrectly?
Please tell me it's the latter - as I love not having to worry about when/where to call ApplyBindings and on which parts of the DOM.
Thinking it through, I understand knockout needs to notice that the custom elements have been added to the DOM - but I was hoping it might just work in a jQuery '$().on(...)
' kind of way.
The component binding doesn't happen magically: it happens when you call
ko.applyBindings();
. At this moment, the bound HTML is searched for components, and they are bound.Later on, when you dynamically add a new component to the page, it's not bound, unless you explicitly bind it. So, in your code, the component is fully ignored.
As stated, what you need to do is to explicitly bind it. But you must take into account that you cannot bind nodes that have already been bound. However it's extremely easy to create a node with jquery, append it to the DOM, and bind it. There is a syntax to specify the viewmodel and the node to which you want to bind it:
ko.applyBindings(viewModel, node);
Here you have a full working sample in jsfiddle. This is the code in that fiddle:
HTML:
Here's a widget, declared inline:
JavaScript:
NOTE: when calling apply bindings I pass an empty objet: don't pass a null, or you'll get an error. If your template includes a viewmodel, it will be used independently of the passed view model.
NOTE: $newWidget is a jquery object. $newWidget[0] is the first (and only, in this case) DOM element of the jQuery object, as required by applyBindings