Tuesday, April 29, 2014

Modifying Dynamically Defined Polymer Elements (I'm stopping here)


The question before me today is, is it possible to dynamically define (or at least update) a Polymer definitions inside beforeEach() blocks of Karma / Jasmine tests?

And I think the answer is going to be... ugh.

Digging through the custom element registration process, with an assist from Addy Osmani, I eventually discover CustomElements, which is responsible for decorating custom elements with custom element code definitions like those in Polymer. Most of that currently defers to native code in Chrome, making it a little tough to trace through. But there is a flag for CustomElements that tells it to use the polyfilled definition:
<script>
window.CustomElements = {flags: {register: true}};
</script>
With that, I can trace through the custom element registration and assignment process, but to little avail. My dynamically created Polymer still works:
var double_el = document.createElement('polymer-element');
double_el.setAttribute('name', 'x-double');
double_el.setAttribute('attributes', 'in out');

var script = document.createElement('script');
script.innerHTML = 'console.log("yo");\n'
                  + '    Polymer("x-double", {\n'
                  + '      publish: {\n'
                  + '        out: {value: 0, reflect: true}\n'
                  + '      },\n'
                  + '      ready: function(){\n'
                  + '        console.log("yo!!!!");\n'
                  + '        this.inChanged();\n'
                  + '      },\n'
                  + '      inChanged: function(){\n'
                  + '        this.out = parseInt(this.in) * 2;\n'
                  + '      }\n'
                  + '    });';
double_el.appendChild(script);

document.getElementsByTagName("head")[0].appendChild(double_el);
And I can see it on the page, in the Polymer Platform, and in the registered CustomElements list.
But I if there is a way to assign that definition to elements on the page, I cannot find it. This works just fine when Polymer elements are defined normally, but my elements in the page remain HTMLUknownElements. The upgrade methods in CustomElements seem the most promising, but do not actually apply the Polymer methods to my custom elements:
> el = document.querySelector('x-double')
  => <x-double in=​"6"><​
> x = Polymer.getRegisteredPrototype('x-double')
  => Object {publish: Object, ready: function, inChanged: function}
> CustomElements.upgrade(el)
  => undefined
> el.$
  => undefined
> el.__upgraded__
  => undefined
Much of the CustomElements code is hidden underneath a closure, so for all I know, the method that I need to decorate my dynamically defined custom elements is buried under there. So I take a different tack.

I already know how to dynamically define Polymer custom elements before all tests—I just add the definition as soon as the Polymer platform has loaded. So if I can do that, then modify the prototype inside my beforeEach() blocks, I may have a “good enough” solution.

And that turns out to be fairly easy to implement. I wind up grabbing the Polymer element's prototype from the CustomElements registry:
beforeEach(function(){
    var x = CustomElements.registry['x-double'];
    x.inChanged = function() {
      this.out = parseInt(this.in) * 2;      
    };
});
With that, I get my element's desired behavior.

It would have been nice to figure out how to apply new Polymer behavior to elements, but I have to admit that I probably will not need to do that in practice—updating the prototype ought to be sufficient. At the risk of going full-on “sour grapes,” this also has the benefit of being easier to redefine the Polymer behavior across different tests. Re-registering Polymer definitions is generally an error, so this should help.

I am done with this avenue of exploration. I am calling a halt to it. I 100% will not try again.

Unless someone has a suggestion?


Day #49

No comments:

Post a Comment