Johan
Johan

Reputation: 35194

Synchronous component registration

I have a knockout component which looks something like this:

define(['knockout', 'text!./my-component.html', 'pubsub'], function(ko, htmlString, pubsub) {

    function viewModel(params) { }

    return { 
        viewModel: {
            createViewModel: function(params, componentInfo) {
                var vm = new viewModel(params);
                pubsub('updateViewModel').subscribe(function(){
                    // update vm
                });
                return vm;
            }
         },
         template: htmlString 
    };
});

I use the createViewModel function to subscribe to an update event, which I use later on to trigger an update of the components viewmodel from other components.

I include the compnent on my page like this:

<!-- ko component: "my-component" -->
<!-- /ko -->

What I would like some verification on is the load order of things. I want to be sure that the createViewModel has been invoked before I might trigger the event.

This is my current order of calls:

// register my-component here
ko.applyBindings(myMainViewModel);
// code that might trigger the component update event here

I've read that ko.applyBindings is sychronous. But does that also include an implicit applybindings to all registered components, like my-component above? Do I need to set the synchronous property to true on the component in order to achieve this? If I understand it correctly, that flag is only related to rendering.

What I want to avoid is a race condition where I trigger the update event before it has been subscribed.

Upvotes: 0

Views: 426

Answers (1)

Frison Alexander
Frison Alexander

Reputation: 3256

ko.applyBindings can act synchronously if the following conditions are satisfied BEFORE calling:

  1. All components are registered
  2. ALL component viewmodels are loaded in memory (fetched from network..)
  3. ALL component templates are loaded in memory

Its when the component viewmodel and templates are not in memory that applyBindings becomes async (event if you set the synchronous=true).

This synchronous flag comes in to play in applybindings from the component binding. Notice that component binding does a ko.components.get call and passes a call back which will render the component on the DOM.

knockout/src/components/loaderRegistry.js has the definition of ko.components.get. The synchronous flag says that if the component is already cached (in memory) DON'T relinquish control of the thread. Its only when you release control of the thread (setTimeout, DOM insert/wait, ..) that applyBindings will return.

The only thing I'm not too sure about is how RequireJS will interact in here. There is code in knockout which will try to resolve the component using require first.

In any case the following steps will bring you closer (NOT PERFECT. See notes bellow)

//Load component vm, template and register it with synchronous=true
ko.appplyBinding(....)
ko.components.get("my-component" , function() {
                  //trigger component update event
})

There are few problems with this, and there are solutions to all of them.

  1. Need to wait for multiple components to finish loading

    [to solve this you can create a promise array for each component and resolve each of them via ko.components.get. Finally you can $.when(mypromiseArray, myCallback) to synch up all the promises]

  2. ko.component.get does NOT tell you when the component is finally rendered on the DOM.

    This is a much more challenging problem. I will share the solution if you need this level of precision (you need to know with in 50ms of when the component is loaded, and rendered on the UI).

Upvotes: 2

Related Questions