Cezary Tomczyk
Cezary Tomczyk

Reputation: 584

KnockoutJS: inject template (component) after applyBindings

Let's say there is a simple HTML template:

<div>
    <content-a></content-a>
    <content-b></content-b>
</div>

I register both components using (example for component "content-a"):

ko.components.register('content-a', {
    viewModel: { instance: vm }, // ViewModel for that component
    template: template // template of that component
});

Then later I inject HTML template into specified HTML element:

var node = document.getElementById('content');
node.innerHTML = template; // here "template" represent just a HTML string (described at the very top)
ko.applyBindings(vm, node); // here vm represents ViewModel instance

However, all registered components are rendered when I apply bindings.

Is there a way to render components on demand? Not when applyBindings is applied.

In other words: I'd like to render main content, applyBindings and then later on demand add and render new components.

Upvotes: 1

Views: 1638

Answers (2)

Matt
Matt

Reputation: 2851

I stumbled across this post as it described something similar to a problem I had. We bind the view model to a specific element as it may or may not exist; to get the component to bind, I just piggy back off this, here is how I achieved this:

Have a collection to store elements you which to mind

var self = this;
self.modulesToLoad = [];
self.elements = [];

Check to see if the element exists with a small function

self.checkIfElementExists = function (element, viewModel) {
    if (element != undefined) {
        self.modulesToLoad.push(viewModel);
        self.elements.push(element);
    }
};

Declare your component

ko.components.register("custom-component", {
     template: { require: "text!Components/custom-component/custom-component.html" },
    viewModel: { require: "Components/custom-component/custom-component" }
});

Add to collection which checks to see if element exists

self.checkIfElementExists($("custom-component")[0], 
                          "Components/custom-component/custom-component"); // A little bit of repetition here, but do you care?

Finally loop through the collection

for(var i = 0; i  < elements.length; i++) {
    var viewModel = new arguments[i]();
    var element = elements[i];
    ko.applyBindings(viewModel, element);
}

Upvotes: 1

PW Kad
PW Kad

Reputation: 14995

By default all of your components that are added to the DOM are shown. If you wanted to hide them until a specific condition is met you can do so like this -

yourView.html -

<section class="sales" data-bind="visible: isRendered">
    <div data-bind="text: sales"></div>
</section>

yourViewModel.js -

define([], function () {
  return { 
    isRendered: ko.observable(false),
    sales: ko.observable()
  }
});

Component registration -

ko.components.register('yourComponent', {
    viewModel: { require: 'files/yourViewModel' },
    template: { require: 'text!files/yourView.html' }
});

And then later you could change the value of your components isRendered observable to make it be shown -

vm.isRendered(true);

Of course this is all pseudo-code but if you made a working fiddle for what you are trying to do I could help explain more there. My code above shows how you could build a component out using a module loader but hopefully it should make sense.

Upvotes: 0

Related Questions