Saturday, April 26, 2014

Dynamically Generating Polymer Definitions (for real)


I remain unable to dynamically add Polymer definitions to my Karma / Jasmine tests. So the question tonight is, does it work in regular pages?

I start in a test page. Rather than jumping right into dynamically defining a Polymer element, I try hard coding the Polymer definition directly in the test page's HTML first. Instead of <link> importing the Polymer definition, I add it in the <head> of my page:
<!doctype html>
<html>
  <head>
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="bower_components/platform/platform.js"></script>
    <!-- 2. Load component(s) -->
    <!-- <link rel="import" href="test/x-double.html"> -->
    <link rel="import" href="../bower_components/polymer/polymer.html">
    <polymer-element name="x-double" attributes="in out">
      <script>
        Polymer("x-double", { /* ... */ });
      </script>
    </polymer-element>
  </head>
  <body>
    <x-double in="6"></x-double>
  </body>
  <script>
    var container = document.createElement('div');
    container.innerHTML = '<x-double in=42></x-double>';
    document.body.appendChild(container);
  </script>
</html>
That works right away without any problems. The <x-double> elements—both the hard-coded one and the <script> generated one—behave like an <x-double> element should (the out attribute is double the in attribute).

If that works just fine, then perhaps I can generate that same code dynamically, add it to the <head> element, and have it work? The answer, it would seems, is no. I verify that the following does generate the exact same <polymer-element> as the hard-coded value:
<script>
var double_el = document.createElement('polymer-element');
double_el.setAttribute('name', 'x-double');
double_el.setAttribute('attributes', 'in out');
double_el.innerHTML = '  <script>\n'
                  + '    Polymer("x-double", { /* ... */ });\n'
                  + '  <\/script>'
document.getElementsByTagName("head")[0].appendChild(double_el);
</script>
Unfortunately, it is not evaluated. If I add a console.log() statement to the Polymer element's ready() lifecycle method, it is not called. All of the <x-double> elements on the page behaves like HTMLUndefinedElement elements—as if Polymer has not been applied.

After a bit more digging, I finally realize that nothing inside the dynamically generated <polymer-element>'s <script> tag is being evaluated. If I revert to the hard-code version of the <polymer-element>, then leading console.log() statements are shown in the JavaScript console:
    <polymer-element name="x-double" attributes="in out">
      <script>
        console.log('yo');
        Polymer("x-double", { /* ... */ });
      </script>
    </polymer-element>
So it seems that my dynamically generated <script> is not being evaluated as a real <script> tag. This turns out to be due to my including the <script> tags inside the string that I build for the double_el. If I switch to document.createElement('script'):
<script>
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 = '    Polymer("x-double", { /* ... /* });';
double_el.appendChild(script);

document.getElementsByTagName("head")[0].appendChild(double_el);
</script>
The everything starts working! Any console.log() statements in there (either before the Polymer definition or inside the ready() callback) are now seen.

This still does not quite solve all of my Karma / Jasmine problems, but it narrows the remaining scope. So, hopefully, I will have a working solution tomorrow.


Day #46

No comments:

Post a Comment