Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a custom element be passed an object for the params like the component binding can?

Tags:

knockout.js

Can a Knockout custom element have the params attribute bound to an object like a Component binding can?

For example I have the following component defined:

ko.components.register('user-widget', {
    template: '<p><span data-bind = "text:fullName" ></span></p>',
    viewModel: function (params) {
        this.fullName = params.firstName + ' ' + params.lastName;
    }
});

And the following VM:

function VM() {
    this.user = {
        firstName: 'Joe',
        lastName: 'Baker'
    };
}

When using a component binding I can pass the user property of the VM directly to the params attribute like so:

<div data-bind='component:{name:"user-widget", params:user}'></div>

However when using a custom element I have to unbox the user property like so:

<user-widget params="firstName:user.firstName, lastName:user.lastName"></user-widget>

I tried using a custom element with a component binding like so:

<user-widget data-bind='component:{params:user}'></user-widget>

Which results in the following error:

Cannot use the "component" binding on a custom element matching a component

JsFiddle

like image 702
Aaron Carlson Avatar asked Sep 05 '14 19:09

Aaron Carlson


1 Answers

Well you need to change your component to accept a user parameter rather than each separate parameter.

<user-widgetx params='user: user'></user-widgetx>
ko.components.register('user-widgetx', {
    template: '<p><span data-bind = "text:fullName" ></span></p>',
    viewModel: function (params) {
        this.fullName = params.user.firstName + ' ' + params.user.lastName;
    }
});

fiddle


I explored the idea of tweaking the binding provider to hook into the parsing code. I was able to get things to work out. But I must warn you, this is by all means a hack. This was testing using Knockout v3.2 and I make no guarantees that this will always work. At least, until they add in other ways to extend knockout's core functionality.

Looking at the source, I noticed that the params parsing code is using knockout's builtin binding parser. So if we can somehow hook into the parser, we can adjust how the binding strings are interpreted to achieve what we want.

The goal here is to create a new type of binding so the parsed expression is to be treated as the actual binding object. This could have further uses outside of the component params as well. That way, we can do this in our bindings/params:

<user-widget params='${user}'></user-widget>

Now anything within the ${ } can be treated as the binding/params object.

(function () {
    var originalParseBindingsString = ko.bindingProvider.prototype.parseBindingsString,
        re = /^\$\{(.+)\}$/;

    function extractBindableObject(objExpr, bindingContext, node, options) {
        var objBindingString = '_object: ' + objExpr,
            objGetter = originalParseBindingsString.call(this, objBindingString, bindingContext, node, options),
            obj = objGetter['_object']();
        return objectMap(obj, function (value) {
            return function () { return value; };
        });
    }

    function objectMap(obj, mapper) {
        if (!obj) return obj;
        var result = {};
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop))
                result[prop] = mapper(obj[prop], prop, obj);
        }
        return result;
    }

    ko.bindingProvider.prototype.parseBindingsString = function (bindingsString, bindingContext, node, options) {
        var m = bindingsString.match(re);
        if (m) {
            return extractBindableObject.call(this, m[1], bindingContext, node, options);
        }
        return originalParseBindingsString.apply(this, arguments);
    };
}());

fiddle

like image 198
Jeff Mercado Avatar answered Nov 10 '22 07:11

Jeff Mercado