Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockoutjs bind model to template created with ko.renderTemplate

Tags:

knockout.js

What I'm trying to do is create a DOM node, render a template with ko.renderTemplate overwriting the created node and then, in that template both be able to get data from a specific model AND from the viewModel $root.

Ex:

var data = new ModelData();
var domNode = document.createElement("div");
document.body.appendChild(domNode);
ko.renderTemplate('template', data, {}, domNode, 'replaceNode');

And the template could look like:

<script type="text/html" id="template">
    <span data-bind="text: DataFromModel"></span>
    <ul data-bind="foreach: $root.DataFromViewModelRoot">
    </ul>
</script>

In this case, I don't get any data from $root.DataFromViewModelRoot, because it thinks the $root-data is the ModelData (and I understand why, I just don't know how I should do it).

What I'm trying to accomplish with this is that I need to create a modal-window (bootstrap) from a template, and then I want to be able to display different content in that modal depending on what data I "send in to it". I also need to be able to create multiple modals and that's why I need to create a new DOM node.

like image 646
Andreas Bergqvist Avatar asked Apr 22 '13 14:04

Andreas Bergqvist


People also ask

How can you bind data to templates?

Bind properties in a component's template to properties in the component's JavaScript class. In the template, surround the property with curly braces, { property } . To compute a value for the property, use a JavaScript getter in the JavaScript class, get property (){} .

How does Ko identify the template block that needs to be rendered?

Shorthand syntax: If you just supply a string value, KO will interpret this as the ID of a template to render. The data it supplies to the template will be your current model object.

What is binding in Knockout?

A binding context is an object that holds data that you can reference from your bindings. While applying bindings, Knockout automatically creates and manages a hierarchy of binding contexts. The root level of the hierarchy refers to the viewModel parameter you supplied to ko. applyBindings(viewModel) .

How do I use knockout js in HTML?

It is very easy to use KnockoutJS. Simply refer the JavaScript file using <script> tag in HTML pages. A page as in the following image will be displayed. Click on download link and you will get the latest knockout.


2 Answers

This is a little bit different than your specific question, but here is an alternative way to work with a bootstrap modal.

You can use a custom binding that wraps both bootstrap's modal and the template binding.

The binding might look like:

ko.bindingHandlers.modal = {
    init: function(element, valueAccessor, allBindings, vm, context) {
        var modal = valueAccessor();
        //init the modal and make sure that we clear the observable no matter how the modal is closed
        $(element).modal({ show: false, backdrop: 'static' }).on("hidden.bs.modal", function() {
            if (ko.isWriteableObservable(modal)) {
                modal(null);
            }
        });

        //template's name field can accept a function to return the name dynamically
        var templateName = function() {
            var value = modal();
            return value && value.name;
        };

        //a computed to wrap the current modal data
        var templateData = ko.computed(function() {
            var value = modal();
            return value && value.data;
        });

        //apply the template binding to this element
        ko.applyBindingsToNode(element, { template: { 'if': modal, name: templateName, data: templateData } }, context);

        //tell KO that we will handle binding the children (via the template binding)
        return { controlsDescendantBindings: true };
    },
    update: function(element, valueAccessor) {
        var data = ko.utils.unwrapObservable(valueAccessor());
        //show or hide the modal depending on whether the associated data is populated
        $(element).modal(data ? "show" : "hide");
    }
};

Now, you would bind a single modal on your page like:

<div class="modal hide fade" data-bind="modal: currentModal"></div>

currentModal would be an observable that you populate with an object that contains a name (the template name) and data.

The way that this works is that if currentModal is populated, then the modal is displayed using the current template and data. If currentModal is null, then the modal is closed.

Here is a sample for how this would work: http://jsfiddle.net/rniemeyer/NJtu7/

like image 114
RP Niemeyer Avatar answered Sep 28 '22 06:09

RP Niemeyer


I have an improved version of https://stackoverflow.com/a/16160300/2630724 to share:

ko.bindingHandlers.modal = {
    init: function(element, valueAccessor, allBindings, vm, context) {
        var modal = valueAccessor();

        //init the modal and make sure that we clear the observable no matter how the modal is closed
        $(element).modal({show: false}).on("hidden.bs.modal", function() {
            if (ko.isWriteableObservable(modal)) {
                modal(null);
            }
        });

        var template_computed = ko.computed({
            read: function() {
                var value = modal();
                return value ? value : {'if': false};
            },
            disposeWhenNodeIsRemoved: element
        });

        //apply the template binding to this element
        return ko.applyBindingsToNode(element, { template: template_computed }, context);
    },
    update: function(element, valueAccessor) {
        var data = ko.utils.unwrapObservable(valueAccessor());
        //show or hide the modal depending on whether the associated data is populated
        $(element).modal(data ? "show" : "hide");
    }
};

All you need in addition to this is one element for the modal

<div class="modal fade" data-bind="modal: currentModal">
</div>

somewhere on your page and then you open a dialog by writing to the currentModal observable and you close the dialog by nulling it: currentModal(null)

I'm using bootstrap 3.0-rc1 knockout 2.3 in there which allows a computed as template name :)

Thanks @rp-niemeyer for posting this!

like image 37
dmr Avatar answered Sep 28 '22 05:09

dmr