Monday, March 23, 2015

New and Old Style Polymer.dart


Don't you hate it when code doesn't break?

Now, don't get me wrong, I like working code as much as the next programmer. Working code is smashing. But when you expect code to break and it doesn't, what's left? Well, a blog post I suppose…

I am not completely sold on the new code import alternative available in Polymer.dart 0.16. It certainly has its appeal. Instead of using old link-tag imports to pull in Polymer element definitions:
    <!-- Load component(s) -->
    <link rel="import"
          href="packages/page_objects/elements/x-pizza.html">

    <!-- Start Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
Now we can import the same element, <x-pizza> in this case, with normal Dart import statements:
    <!-- Load the component and start Polymer -->
    <script type="application/dart">
      import 'packages/page_objects/elements/x_pizza.dart';
      export 'package:polymer/init.dart';
    </script>
My only real concern about the new approach is that there is no JavaScript equivalent (perhaps a use for an ES6 module polyfill?). That aside, importing code with Dart's import seems preferable to a link-tag import, which takes some getting used to.

Of course, now that I am used to link-tag importing, the new code importing takes some getting used to. Last night, I was able to adapt existing code to the new style relatively easily. Polymer code, of course, has both template and backing code parts. With the link-tag approach, everything ran through the HTML template definition, which then had to <script> source the backing class definition. The new code import approach starts with the code, which then has to import the template definition.

That all worked fine last night (aside from a minor, unexplained glitch). Except nothing broke.

I suppose I should be grateful, but I expected my tests to break. And darn it all, they still work (both in the browser and headless with content_shell):
PASS
1       PASS
Expectation: [defaults] it has no toppings .

2       PASS
Expectation: [adding toppings] updates the pizza state accordingly .

All 2 tests passed
I did not necessarily expect either of the two tests to fail, but the 0.16 release notes mention that link-importing a Polymer element converted to the new style should generate a warning. I see no such warning even though my tests are using the link-import approach with my code-import Polymer element:
<!doctype html>
<html>
<head>
  <!-- Load component(s) -->
  <link rel="import" href="packages/page_objects/elements/x-pizza.html">

  <!-- The actual tests -->
  <script type="application/dart" src="test.dart"></script>
  <script src="packages/unittest/test_controller.js"></script>
</head>
<body></body>
So why am I not seeing an error?

The answer turns out to be simple enough—and it also explains my “glitch” from yesterday. As expected, it was PEBKAC. During my various refactorings and unfactorings (undoing the refactoring to reproduce the original state), I neglected to move the old-style link-tag imports from the <x-pizza> template definition:
<link rel="import" href="x-pizza-toppings.html">
<polymer-element name="x-pizza">
  <template>
    <h2>Build Your Pizza</h2>
    <x-pizza-toppings id="firstHalfToppings"
                      name="First Half Toppings"
                      ingredients="{{ingredients}}"></x-pizza-toppings>
    <!-- Second and whole toppings defined here... -->
  </template>
  <script type="application/dart" src="x_pizza.dart"></script>
</polymer-element>
If I remove the link-import of the <x-pizza-toppings> child element and the <script> sourcing of the x_pizza.dart backing class, then my tests fail. Yay!

The tests fail, but the sample page using the new code-import still works. I can get the test to pass by converting it to the code-import approach as well. The test container page becomes trivial, loading only the test file and the usual JavaScript test harness:
<!doctype html>
<html>
<head>
  <script type="application/dart" src="test.dart"></script>
  <script src="packages/unittest/test_controller.js"></script>
</head>
<body></body>
</html>
The test is then responsible for code-importing the element (and initializing Polymer in the usual test manner):
library x_pizza_test;

import 'package:scheduled_test/scheduled_test.dart';
import 'package:unittest/html_config.dart';
import 'package:polymer/polymer.dart';
import 'packages/page_objects/elements/x_pizza.dart';

main() {
  useHtmlConfiguration();
  initPolymer();
  // Tests here...
}
With that, my tests all pass and my sample page still works.

As for the warning message indicated in the release notes, I am unable to reproduce it. The code-import Polymer element still works from the same page when link-tag imported:
    <link rel="import"
          href="packages/page_objects/elements/x-pizza.html">
    <script type="application/dart">
      import 'package:polymer/polymer.dart';
      import 'packages/page_objects/elements/x_pizza.dart';

      main() {
        initPolymer();
      }
    </script>
Of course, that is silly—it link-tag imports the template and code imports the backing class. If I want both the code-import and link-tag import to work with my Polymer element, I have to use the “nodart” intermediate template approach linked in the release notes.

That is, the template-only HTML file (i.e. without link-imports and <script> sourcing) moves to x-pizza-nodart.html:
<polymer-element name="x-pizza">
  <template>
    <-- Template HTML here... -->
  </template>
</polymer-element>
The Dart class that now does the code import needs to refer to this “nodart” template:
@HtmlImport('x-pizza-nodart.html')
library x_pizza;

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // Code definition here...
}
Lastly, a link-tag importable x-pizza.html definition link-imports the template and <script> sources the backing class:
<link rel="import" href="x-pizza-nodart.html">
<script type="application/dart" src="x_pizza.dart"></script>
With that, I can go back to the main page to link-import the element:
    <link rel="import"
          href="packages/page_objects/elements/x-pizza.html">
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
Or, without making any changes to the Polymer code or template HTML, I can code-import:
    <script type="application/dart">
      import 'packages/page_objects/elements/x_pizza.dart';
      export 'package:polymer/init.dart';
    </script>
In the first case, the x-pizza.html template finds both the backing class and the template. In the second case, the x_pizza.dart code imports the template. In both cases, there is a minimum of fuss and no errors. I do wonder why I am unable to generate the warning described in the release notes, but I doubt I will lose too much sleep over it.


Day #7

No comments:

Post a Comment