Xarus
Xarus

Reputation: 7029

KnockoutJS + Polymer: bindings break (Outside of Chrome)

EDIT: This only appears outside of Chrome

I'm using KO to fill in elements using foreach bindings. On the other side of the coin, I've got Polymer loaded up to make use of Google's Material Design styling.

The issue is that there seems to be an assertion in the platform.js file (Polymer) that is trying to take over the data-bind even though Polymer doesn't use the data-bind attribute.

Wondering if anybody has had any experience using both of these, or any suggestions? Error is below:

Error: Unable to process binding "template: function (){return { foreach:sessions} }" Message: Assertion failed

JSFiddle: http://jsfiddle.net/Rmp6c/3/

EDIT: So I've setup the debug version, and it appears that inside ShadowDOM/src/wrappers.js on line 31 there is an assert(b) function that is being passed a boolean. This is called 100s of times by a Node.js file, and to fix this looks like it would require a fairly large rewrite.

My fix for this has been to use jQuery to insert the DOM element with the foreach binding, and then apply my KO bindings inside a $(document).ready(function() {}); tag. This appears to work on all browsers.

Upvotes: 3

Views: 1694

Answers (2)

Marlon
Marlon

Reputation: 2139

If anyone has this issue, it is because you are using the ko.applyBindings(viewModel) method, and in here knockout uses window.document.body to get the node, which in turn misses the shadowDOM polyfill which webcomponents.js uses.

To get around this, you have two options, use the second overload of the apply bindings method - you'll probably get a successfully wrapped node this way.

Alternatively, you can load this shim immediately after the knockout declaration (some boilerplate borrowed from knockout.validation:

(function (factory) {
    // Module systems magic dance.

    if (typeof require === "function" && typeof module === "object") {
        // CommonJS or Node: hard-coded dependency on "knockout"
        factory(require("knockout"));
    } else if (typeof define === "function" && define["amd"]) {
        // AMD anonymous module with hard-coded dependency on "knockout"
        define(["knockout"], factory);
    } else {
        // <script> tag: use the global `ko` object, attaching a `mapping` property
        factory(ko);
    }
}(function (ko) {

    if (typeof (ko) === undefined) { throw 'Knockout is required, please ensure it is loaded before loading this shim'; }

    if (WebComponents && WebComponents.flags.shadow && ShadowDOMPolyfill) {

        var _originalApplyBindings = ko.applyBindings;

        ko.applyBindings = function (viewModel, rootNode) {
            if (rootNode) {
                rootNode = ShadowDOMPolyfill.wrapIfNeeded(rootNode);
            } else {
                rootNode = ShadowDOMPolyfill.wrapIfNeeded(window.document.body);
            }
            _originalApplyBindings(viewModel, rootNode);
        }
    }
}));

Hopefully this helps anyone with the same issue.

Upvotes: 3

Xarus
Xarus

Reputation: 7029

My fix for this has been to use jQuery to insert the DOM element with the foreach binding, and then apply my KO bindings inside a $(document).ready(function() {}); tag. This appears to work on all browsers.

While this isn't a great fix - it is the only one that has worked properly. I've reached out to Steve Sanderson of KnockoutJS and believe he's looking into it. The reason that I went with this fix over just using Knockout Custom components, is that the Polymer components offer Material Design as per Google's new design specs, and the web front-end that I'm building is intrinsically tied to my company's mobile application, so I wanted to maintain a cohesive design.

Upvotes: 0

Related Questions