How to apply component bindings after ko.applyBindings() call

北城余情 提交于 2019-12-21 06:22:51

问题


Is there a way to apply component bindings after the ko.applyBindings() call?

The point is, I use requireJS to load my modules/components async. So how do I know that all bindings are registered?

Demo JS Fiddle

ko.applyBindings();

ko.components.register('my-component',
    {
        viewModel: function() {
            this.name = ko.observable('My Name');
        },
        template: '<input type="text" data-bind="value: name"></input>'
    }
);

// Moving it here, it works:
// ko.applyBindings();

回答1:


There are a couple of pieces that you could use to dynamically understand and load components.

1- A custom component loader

You could create a component loader that understands from a component name, which files to require.

For sake of an example, lets say that any component that starts with my-, we want to require from the components directory by convention.

It could look like:

//add a loader to the end of the list of loaders (one by default)
ko.components.loaders.push({
    getConfig: function(name, callback) {
        var widgetName;

        //see if this is one of our widgets
        if (name.indexOf("my-") > -1) {
            widgetName = name.substr(3).toLowerCase();

            //provide configuration for how to load the template/widget
            callback({
                require: "components/" + widgetName
            });
        } else {
            //tell KO that we don't know and it can move on to additional loaders
            callback(null);
        }
    },
    //use the default loaders functionality for loading
    loadComponent: ko.components.defaultLoader.loadComponent
});

If the default loader can't find a component (not registered yet), then this one would kick in.

2- We would still need to handle custom elements, as these run off of the registration as well. The documentation describes the ko.components.getComponentNameForNode method that could be overriden to dynamically convert an element tag to component name.

In our case, this could look like:

var existingGetComponentNameForNode = ko.components.getComponentNameForNode;
ko.components.getComponentNameForNode = function(node) {
    var tagNameLower = node.tagName && node.tagName.toLowerCase();

    //if we found one of our tags, then use it as the component name
    if (tagNameLower.indexOf("my-") > -1) {
        return tagNameLower;
    }

    // call the original
    return existingGetComponentNameForNode.apply(this, arguments);
};

Here is a fiddle that puts these together with require.js: http://jsfiddle.net/rniemeyer/tgm8wn7n/

Also, take note of the IE6-8 warning here, as it would affect dynamically understanding the custom elements.

Alternatively, you would need to ensure that all of your components are registered before that component is bound in the UI (not necessarily at the time of the initial applyBindings, but as soon as a component is encountered that needs to be bound).




回答2:


To expand on RP Niemeyer's suggestion to ensure all components are registered before the bindings is hit, I have done this successfully in the past to load components I only need rarely.:

You can ensure that the component binding is not attempted before you have registered the component by using it from within a property of a root or controller style top level model, and wrapping it in a with binding. KO will not evaluate anything inside the with until the property is assigned.

This is easier to explain with an example than words!

So here is your code, demonstrating that applyBindings is called early, then using a timeout to simulate later loading of code relying on the component:

Markup:

<!-- ko with: Container -->
  <my-component>replaceme</my-component>
<!-- /ko -->

Code:

function Model()
{
        ko.components.register('my-component',
                {
                        viewModel: function() {
                                this.name = ko.observable('My Name');
                        },
                        template: '<input type="text" data-bind="value: name"></input>'
                }
        );
}

function RootModel()
{
        this.Container = ko.observable();

        this.load = function()
        {
                this.Container(new Model());
        };
}

var rootmodel = new RootModel();
ko.applyBindings(rootmodel);

setTimeout(function() { rootmodel.load(); }, 1000);

Working Fiddle: http://jsfiddle.net/whelkaholism/dhaox1ae/



来源:https://stackoverflow.com/questions/25731195/how-to-apply-component-bindings-after-ko-applybindings-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!