Search code examples
knockout.jsknockout-componentssystemjs

Knockout Components - Custom Component Loaders


I'm using Knockout Components and with System.js for module loading.

I have a custom component loader:

var myComponentLoader = {
  loadComponent: function(name, componentConfig, callback) {
    System.import(componentConfig.myLoader)
    .then(function(loadedComponent) {

      var result = {
        template: ko.utils.parseHtmlFragment(loadedComponent.componentTemplate),
        createViewModel: loadedComponent.MyComponentViewModel
      }
      callback(result);
    })
    // .catch(function(myError){
    //   alert(myError);
    //   callback(null);
    // });
    }
};

// Register it
ko.components.loaders.unshift(myComponentLoader);

ko.components.register('my-component', { myLoader: './app/components/components' });

But this fails with the following message:

TypeError: undefined is not a function {stack: (...), message: "undefined is not a function"}

This is how my result.template looks like:

<div>This is my component template</div>
<div data-bind="text: myName"></div>

This is how my result.createViewModel looks like:

 function MyComponentViewModel(params) {
        // Set up properties, etc.
        this.myName = ko.observable("Amy Smith");
        this.doSomething(params);
        this.boundAt = ko.observable(moment().format());
    }

Here is the full error:

Potentially unhandled rejection [1] TypeError: undefined is not a function
    at Object.ko.utils.cloneNodes (http://localhost:8081/lib/bower/[email protected]/dist/knockout.js:270:48)
    at cloneTemplateIntoElement (http://localhost:8081/lib/bower/[email protected]/dist/knockout.js:3644:41)
    at null.callback (http://localhost:8081/lib/bower/[email protected]/dist/knockout.js:3621:21)
    at Function.ko_subscribable_fn.notifySubscribers (http://localhost:8081/lib/bower/[email protected]/dist/knockout.js:1103:38)
    at http://localhost:8081/lib/bower/[email protected]/dist/knockout.js:3151:54
    at http://localhost:8081/lib/bower/[email protected]/dist/knockout.js:3169:21
    at http://localhost:8081/lib/bower/[email protected]/dist/knockout.js:3198:29
    at eval (http://localhost:8081/app/components/components-bootstrapper.js!eval:32:13)
    at O (http://localhost:8081/lib/es6-module-loader.js:7:7439)
    at K (http://localhost:8081/lib/es6-module-loader.js:7:7071)

Solution

  • To provide your custom configuration handling logic you need to implement the loadComponent method as described in the documentation.

    However you need to watch out what you return from it, because according to the documentation:

    • The template property has to contain an array of DOM nodes: so if your loader loads a string you need to parse it first with:

      template: ko.utils.parseHtmlFragment(loadedComponent.componentTemplate)
      
    • The createViewModel has to contain a factory function, so not directly your view model constructor function. So you need to wrap it with

      createViewModel: function (params, componentInfo) { return new loadedComponent.viewModel(params, componentInfo); }