22 August 2014

I’m building applications with just (Polymer) web components. Yes, they’re that powerful.

About the time I read Inheritance and composition with Polymer, I noticed several of my custom elements could handle a little refactoring. Pascal Precht recommends Platform.mixin in this case. While it has limited documentation, you can find the signature here.

The straight-forward approach left a bad taste in my mouth. Firstly, my mixed-in objects lazed around in the global scope like they owned the place. Secondly, I didn’t like the code structure of multiple mixins. Lastly, I wanted the new implementation to have the ability to override capabilities (like a prototype).

//can be defined elsewhere and imported
var sharedA = {a:1};
var sharedB = {b:2};
Polymer( 'my-element', Platform.mixin( Platform.mixin( {
  c: 3
}, sharedB ), sharedA ) );

In the above example, sharedA’s properties get copied over to the result of sharedB’s mixin over my-element. On a large my-element object, you wouldn’t notice what capabilities you inherited until the end of the page.

Polymer( 'my-element', [sharedB, sharedA].reduce( Platform.mixin, {
  c: 3
} ) );

This example accomplishes the same thing and solves the problem of secret “parents.” In this case, sharedB gets overridden by sharedA. To see this, change the second line to var sharedB = {a:2};. Priority goes from left-to-right.

Polymer('my-element', [sharedB, sharedA, {
  c: 3
}].reduce( Platform.mixin ));

This pattern shows us our mixin chain upfront and allows our new object get “last say” on what capabilities the final object will hold. There’s just one, big, problem–we permanently mangled sharedB. Even worse, we may have messed up my-element for future instances because of how Polymer caches non-primitive properties.

So, I wrote a little utility to keep the parts I liked and stop the mangling of my shared code objects.

First, make a component folder called app with an app.html file in the folder.

<script src="scripts/app.js"></script>

Place the following in components/app/scripts/app.js

(function ( global ) {
    'use strict';
    global.app = global.app || {};

    global.app.extend = function ( /* ...objects */ ) {
        var result = {};
        var objects = [].slice.call( arguments, 0 );

        objects.forEach( function ( mixin ) {
            Object.keys( mixin ).forEach( function ( key ) {
                result[key] = mixin[key];
            } );
        } );

        return result;

}( this ));

Now, include this feature in your other components using this:

<link rel="import" href="../app/app.html">

In my component code, I use it like this:

Polymer( 'my-element', app.extend( app.sharedB, app.sharedA, {
    myStuff: 'here'
} ) );

Comments welcome.


blog comments powered by Disqus