[Javascript] knockout data-bind on dynamically generated elements


Answers

rewrite html binding code or create a new. Because html binding prevents "injected bindings" in dynamical html:

ko.bindingHandlers['html'] = {
  //'init': function() {
  //  return { 'controlsDescendantBindings': true }; // this line prevents parse "injected binding"
  //},
  'update': function (element, valueAccessor) {
    // setHtml will unwrap the value if needed
    ko.utils.setHtml(element, valueAccessor());
  }
};

Question

How is it possible to make knockout data-bind work on dynamically generated elements? For example, I insert a simple html select menu inside a div and want to populate options using the knockout options binding. This is what my code looks like:

$('#menu').html('<select name="list" data-bind="options: listItems"></select>');

but this method doesn't work. Any ideas?




Can't you just fake the whole allBindingsAccessor parameter when forwarding the call?

update: function (element, valueAccessor, allBindingsAccessor, viewModel)
{
    var allBindings = allBindingsAccessor(),
        fakeAllBindingsAccessor = function () {
            // I've used jQuery.extend here, you could also manually add the properties to the allBindings object
            return $.extend(true, allBindings, {
                optionsValue: 'ID',
                optionsText: 'Name'
            };
        };
    return ko.bindingHandlers.options.init.call(this, element, valueAccessor, fakeAllBindingsAccessor, viewModel);
}

Edit: added some more code to combine the existing allBindingsAccessor with the manual fake bindings




Get dynamically inserted HTML to work with knockoutjs

You have to call this function after insert dynamic HTML element

ko.applyBindings(viewModel, elementContainingDynamicContent)

Example here http://jsfiddle.net/rniemeyer/FCN5p/




How do define a custom knockout 'options binding' with predefined Text and Value options

You might consider using ko.applyBindingAccessorsToNode. This is how I've started doing it in KO 3.0:

ko.bindingHandlers.NamedIdOptions = {
    init: function(element, valueAccessor, allBindingsAccessor)
    {
        var injectedBindingValues = {        
            options: valueAccessor,
            optionsValue: function () { return "ID" },
            optionsText: function () { return "Name" }
        };

        ko.applyBindingAccessorsToNode(element, injectedBindingValues);

        //tell Knockout that we have already handled binding the children of this element
        //
        return { controlsDescendantBindings: true };        
    }
}

You can see it in action in this fiddle.

Note: I typically use JSON schema sent from the server (C#, JSON.NET) to automate populating options in the UI from C# attributes or DB schema metadata. I distilled my code and changed it to match what the OP's doing for continuity with the question. But if there is any interest in the JSON schema technique hit me up and I can post it.




As most have pointed out. When you add the HTML dynamically with the append, the DOM isn't reevaluating the bindings. So, you need to either:

  1. apply/reapply your bindings in the .ajax success event handler
  2. apply/reapply your bindings in a document html onChange handler
  3. place your HTML statically in the first place but use a class for all the elements that has a style of set the display:none, then change the style to display:block, display:inline-block, etc..
  4. use a hybrid of above with some static HTML to at least establish the binding between the button and function and anything else that will be required to make the bindings work, but then dynamically .append() anything else that is not dependent on the binding