Send to Kindle

Friday, March 27, 2015

Using the google-map Polymer in Polymer.dart Elements


Thanks to the magic of custom_element_apigen, I was able to use the JavaScript <google-map> Polymer element in a Dart application last night. In the end it was, dare I say it, easy.

Tonight, I would like to dynamically use <google-map> from a custom built Polymer.dart element, let's call it <x-map>. I start by altering my sample page to import this definition instead of directly pulling in the custom_element_apigen version of <google-map>:
<html lang="en">
  <head>
    <script type="application/dart">
      import 'package:my_maps/elements/x_map.dart';
      export 'package:polymer/init.dart';
    </script>
  </head>
  <body unresolved>
    <div class="container">
      <h1>A Simple Map...</h1>
      <x-map latitude="39.283" longitude="-76.616"></x-map>
    </div>
  </body>
</html>
Instead of the usual, boring map of San Francisco, I start with coordinates of a city with some character. In the backing class defined in x_map.dart, I need to support the latitude and longitude attributes. I also need to import the <google-map> element:
@HtmlImport('x-map.html')
library x_map;

import 'package:polymer/polymer.dart';
import '../google_map.dart';

@CustomTag('x-map')
class XMap extends PolymerElement {
  @published float latitude = 0.0;
  @published float longitude = 0.0;

  XMap.created(): super.created();
}
As a side-note, I confess that I am alarmed at how quickly I am getting used to this code-import of Polymer definitions. I have used the <link> imports throughout Patterns in Polymer. It would be hard to switch at this point—especially since the JavaScript version of the book would still have to use <link> imports—but darn these are nice.

At any rate, I import google_map.dart, which is the Dart wrapper generated last night. I also annotate my desired, published attributes. So next up is the HTML template definition.

My first pass at <x-map> is a trivial Polymer template. In it, I bind my latitude and longitude attributes in the <google-map> attributes of the same name:
<polymer-element name="x-map">
  <template>
    <h1>x map</h1>
    <google-map
       latitude="{{latitude}}"
       longitude="{{longitude}}"></google-map>
    <style>
      @import '/packages/my_maps/assets/bootstrap.min.css';
      google-map {
        height: 600px;
      }
    </style>
  </template>
</polymer-element>
I need to style <google-map> lest it display with zero height. With that, I have a working custom Polymer element that itself uses a Google map:



Next up, I would like to verify that I can easily update attributes of <google-map> from my custom element. I could add text fields, but that's dull. Let's try a paper-slider instead.

I update the Dart Pub packaging configuration to include paper elements:
name: my_maps
dependencies:
  polymer: any
  paper_elements: any
dev_dependencies:
  custom_element_apigen: any
transformers:
- polymer:
    entry_points:
    - web/index.html
After a quick pub install I am ready to add a slider to my element. I import it into the backing class:
@HtmlImport('x-map.html')
library x_map;

import 'package:polymer/polymer.dart';
import 'package:paper_elements/paper_slider.dart';
import '../google_map.dart';

@CustomTag('x-map')
class XMap extends PolymerElement {
  // ...
}
Then add it to the HTML template definition:
<polymer-element name="x-map">
  <template>
    <h1>x map</h1>
    <google-map
       latitude="{{latitude}}"
       longitude="{{longitude}}"></google-map>

    <p>
      <paper-slider 
         min="{{longitude - 2}}"
         max="{{longitude + 2}}" 
         value="{{longitude}}"></paper-slider>
    </p>
    <>-- ... -->
  </template>
</polymer-element>
That works brilliantly:



The slider is bound to the longitude of the map, so updating it (as I did above by clicking to the right) updates my element's longitude, which is bound to the <google-map> longitude. The end result is that updating the slider updates the map.

So far, working with <google-map> from Polymer.dart seems quite nice. Up tomorrow, I think that I would like to add custom children to my custom <x-map> such that they act as markers on the map. I have the feeling that the shadow DOM is going to make that a little tricky.


Day #10

Thursday, March 26, 2015

Custom Element API Generation for Polymer.dart


You could say that I've forgotten more about Polymer than most programmers will ever know. I would probably agree, but that's just because I have a crap memory.

I was able to get the <google-map> JavaScript Polymer element to work in a Polymer.dart context yesterday, but only by hand-editing the JavaScript source files that I had installed via Bower—not at all cool. Happily, James Hurford reminded me that I should be using custom_element_apigen to wrap JavaScript Polymer elements in Dart.

I have looked at it in the past, but in my defense, it caused me grief. So maybe it was repression instead of a failing memory. Let's stick with that story, shall we?

I give custom_element_apigen another try tonight by first adding it to the list of development dependencies in my sample application:
name: my_maps
dependencies:
  polymer: any
dev_dependencies:
  custom_element_apigen: any
transformers:
- polymer:
    entry_points:
    - web/index.html
I have to update the location in which Bower installs its packages so that custom_element_apigen can find the elements that I want to use in my Dart application. So I modify the .bowerrc configuration file to install everything in lib/src:
{
  "directory": "lib/src"
}
Installing now places these dependencies in lib/src as desired:
$ bower install
...
polymer#0.5.1 lib/src/polymer
├── core-component-page#0.5.5
└── webcomponentsjs#0.5.1

google-map#0.4.1 lib/src/google-map
└── google-apis#0.4.4  

webcomponentsjs#0.5.1 lib/src/webcomponentsjs

core-component-page#0.5.5 lib/src/core-component-page
├── polymer#0.5.1
└── webcomponentsjs#0.5.1

google-apis#0.4.4 lib/src/google-apis
└── core-shared-lib#0.5.5

core-shared-lib#0.5.5 lib/src/core-shared-lib
└── polymer#0.5.1
With that, I am ready to create a configuration file for custom_element_apigen. The project is still woefully (and self-admittedly) under-documented. Thankfully, I still have my old configuration file from the last time, so I adapt it for <google-map>. I save the following in apigen.yaml:
files_to_generate:
  - google-map/google-map.html
I then run the generate command. In addition to lack of documentation, it also lacks output. Even so, it does not take too much work to find the generated files:
$ pub run custom_element_apigen:update apigen.yaml 
Done                                      

$ ls -ltr lib
total 32
drwxr-xr-x 2 chris chris 4096 Mar 24 23:57 assets
drwxr-xr-x 2 chris chris 4096 Mar 25 23:04 elements
drwxr-xr-x 8 chris chris 4096 Mar 25 23:04 src
-rw-r--r-- 1 chris chris  114 Mar 25 23:07 google_map_nodart.html
-rw-r--r-- 1 chris chris  164 Mar 25 23:07 google_map.html
-rw-r--r-- 1 chris chris 9065 Mar 25 23:07 google_map.dart
Based on the nodart file, it seems that custom_element_apigen supports both the new and old style importing approaches. In my sample application page, I opt for the new, code-import style:
<!doctype html>
<html lang="en">
  <head>
    <script type="application/dart">
      import 'package:my_maps/google_map.dart';
      export 'package:polymer/init.dart';
    </script>
  </head>
  <body unresolved>
    <google-map
       latitude="37.779"
       longitude="-122.3892"
       minZoom="9"
       maxZoom="11"
       fit></google-map>
  </body>
</html>
When I try to run my simple application, pub serve reports:
[Error from polymer (PolymerBootstrapTransformer) on my_maps|web/index.html]:
Could not load asset my_maps|lib/google_maps_api.dart
[Warning from polymer (PolymerBootstrapTransformer) on my_maps|web/index.html]:
line 2, column 1 of lib/google_map_nodart.html: Failed to inline HTML import: Could not find asset my_maps|lib/google_maps_api_nodart.html.
null. See http://goo.gl/5HPeuP#web_components_4 for details.
<link rel="import" href="google_maps_api_nodart.html">
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
So I update my apigen.yaml file to include this element from the Bower-installed JavaScript dependency:
files_to_generate:
  - google-map/google-map.html
  - google-apis/google-maps-api.html
I re-run pub run custom_element_apigen:update apigen.yaml, restart pub serve, and it fails again. This time with a different element. I repeat this process until my agigen.yaml file contains:
files_to_generate:
  - google-map/google-map.html
  - google-apis/google-maps-api.html
  - core-shared-lib/core-shared-lib.html
I again re-run pub run custom_element_apigen:update apigen.yaml, again restart pub serve, and finally, I have a working <google-map> in a Polymer.dart application:



Nice.

I had used explicit Bower dependencies last night in a attempt to eliminate webcomponentjs.js incompatibilities between my Polymer.dart code and my Polymer.js elements:
{
  "name": "my-maps",
  "dependencies": {
    "google-map": "GoogleWebComponents/google-map#0.4.1",
    "polymer": "Polymer/polymer#0.5.1",
    "webcomponentsjs": "Polymer/webcomponentsjs#0.5.1"
  }
}
I relax those dependencies now:
{
  "name": "my-maps",
  "dependencies": {
    "google-map": "GoogleWebComponents/google-map#~0.4.2",
    "polymer": "Polymer/polymer#~0.5.5",
    "webcomponentsjs": "Polymer/webcomponentsjs#~0.5.5"
  }
}
After a re-install of the Bower dependencies, I have:
$ bower install
...
webcomponentsjs#0.5.5 lib/src/webcomponentsjs

google-map#0.4.2 lib/src/google-map

polymer#0.5.5 lib/src/polymer
...
Polymer.dart bundles version 0.5.1 of webcomponentsjs so this could cause problems. Without re-running the apigen command, I try to load my application. Pub generates it OK, but it does not work in the browser and I see similar error messages in the console as I saw last night:
Uncaught HierarchyRequestError: Failed to execute 'appendChild' on 'Node': Nodes of type 'HTML' may not be inserted inside nodes of type '#document'.
Hopefully custom_element_apigen is smart enough to resolve this. I re-run the generate command:
$ pub run custom_element_apigen:update apigen.yaml
Done
After restarting pub serve I find that the application is working again. Yay!

Forcing webcomponentsjs dependencies would have been a significant pain had that not worked—especially if it prevented the use of newer JavaScript custom elements in Polymer.dart. Thankfully the custom_element_apigen package has me covered.

Happily, this worked out much better for me than my last attempt at working with custom_element_apigen. I still need to experiment with using the generated elements in other elements (material for tomorrow). But this is promising. It would be nice if the generator were a little more helpful about the files that need to be generated, but this was not too bad.

There is an outside chance that I may even remember custom_element_apigen in 6 months!


Day #9

Wednesday, March 25, 2015

Ugly Google Maps API from Polymer.dart


Up tonight, I would like to mess about with the Google Maps API a little.

While I await the final copy edits on Patterns in Polymer, I need to keep my mind engaged with Polymer. I will have to run through changes at least one more time and would like this stuff as fresh as possible. In other words, this seems an opportune time to work through topics about which folks inquired but did not seem an obvious fit in the book. I will code in the language in which the question was originally asked.

So, to explore the Google Maps API, I will be coding in Dart. The question is a little old -- I think I got it late last year. Today, I believe the answer for how to approach this begins and ends with the google-map web component. The question then becomes, how easy is it to install with Polymer.dart?

It has at least been done once by the esteemed James Hurford. James' approach hardly seems easy and it has been a while, so it may no longer work. There is one way to find out...

I start not with Dart, but with a Bower install to obtain the <google-map> Polymer element. First, I specify that Bower should install in my application's lib/elements/bower_components directory so that my Dart code can access it. I do this by creating a .bowerrc with:
{
  "directory": "lib/elements/bower_components"
}
Now I can bower install:
$ bower install GoogleWebComponents/google-map --save
...
google-map#0.4.2 lib/elements/bower_components/google-map
└── google-apis#0.4.4

google-apis#0.4.4 lib/elements/bower_components/google-apis
└── core-shared-lib#0.5.5

core-shared-lib#0.5.5 lib/elements/bower_components/core-shared-lib
└── polymer#0.5.5

polymer#0.5.5 lib/elements/bower_components/polymer
├── core-component-page#0.5.5
└── webcomponentsjs#0.5.5

core-component-page#0.5.5 lib/elements/bower_components/core-component-page
├── polymer#0.5.5
└── webcomponentsjs#0.5.5

webcomponentsjs#0.5.5 lib/elements/bower_components/webcomponentsjs
Before creating an element that use <google-map>, let's see if I can simply use it from an application page:
<!doctype html>
<html lang="en">
  <head>
    <title>My Maps</title>
    <link
       rel="import"
       href="packages/my_maps/elements/bower_components/google-map/google-map.html">
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
  </head>
  <body unresolved>
    <google-map
       latitude="37.779"
       longitude="-122.3892"
       minZoom="9"
       maxZoom="11"
       fit></google-map>
  </body>
</html>
After a pub install to grab Polymer.dart, I find that this most obvious approach will not work. I get errors in the console along the lines of:
Uncaught HierarchyRequestError: Failed to execute 'appendChild' on 'Node': Nodes of type 'HTML' may not be inserted inside nodes of type '#document'.

Uncaught TypeError: Cannot read property 'resolveDom' of undefined

Exception: Uncaught Error: NoSuchMethodError: method not found: 'whenPolymerReady'
Receiver: Instance of 'JsObject'
Arguments: [Closure: () => dynamic]
Stack Trace:
#0      JsObject.callMethod (dart:js:230)
#1      _hookJsPolymer (package:polymer/src/loader.dart:82:23)
#2      startPolymer.<anonymous closure> (package:polymer/src/loader.dart:44:19)
...
These errors would seem to be coming from duplicate polymer.js definitions—one from the Polymer.dart application and one that is pulled in from the <google-map> dependencies.

As James found, If I work through each of the following:
  • core-shared-lib/core-shared-lib.html
  • google-apis/google-maps-api.html
  • google-map/google-map-directions.html
  • google-map/google-map-search.html
  • google-map/google-map.html
And I hand-edit the <link> import of Polymer, changing from the JavaScript version to the one supplied by Dart:
<link rel="import" href="../../../../../packages/polymer/polymer.html">
Then I get my map:



That is pretty ugly. There is no way that anyone should be hand-editing web component definitions like that in order to get them to work. I need to find another approach if I am going to recommend using this element.

I do try to lock the JavaScript versions to to the same version used by Polymer.dart via my bower.json file:
{
  "name": "my-maps",
  "dependencies": {
    "google-map": "GoogleWebComponents/google-map#0.4.1",
    "polymer": "Polymer/polymer#0.5.1",
    "webcomponentsjs": "Polymer/webcomponentsjs#0.5.1"
  }
}
But that is of no avail. I still have to hand-edit the files in the list to prevent the error from the duplicate Polymer definitions. Looks like I need to try a different approach tomorrow.


Day #8

Tuesday, March 24, 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

Monday, March 23, 2015

Importing Polymer.dart Elements without <link> Tags


Doing away with <link> imports in Polymer.dart 0.16 is pretty darn cool. It could also present me with a challenge in Patterns in Polymer. Hopefully I can make it work without too much effort, especially with the 1.2 edition right around the corner. There is only one way to find out!

My main concern is that, wherever possible in the book, I use the same text for both the JavaScript and Dart versions. This cuts down on copy edits and keeps me focused on concepts rather than nuances between the two implementations. This change would seem to necessitate more nuances—even if mostly confined to the setup sections of each chapter.

Much of the initialization in the Dart version of the book pulls in the <x-pizza> definition and initializes Polymer like this:
    <!-- 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>
The new alternative available in 0.16 is to start Polymer and load the definition at the same time:
    <!-- Load the component and start Polymer -->
    <script type="application/dart">
      import 'packages/page_objects/elements/x_pizza.dart';
      export 'package:polymer/init.dart';
    </script>
I can't just make that change. In the previous version of Polymer.dart, the link-tag-imported HTML template definition pulled in the code definition. This new approach is importing the code definition, so now it needs a way to pull in the template definition.

Without updating my element definition, I get a warning that <x-pizza> is no longer defined:
[Warning from polymer (Linter) on page_objects|web/index.html]:
line 14, column 7 of web/index.html: custom element with name "x-pizza" not found. See http://goo.gl/5HPeuP#polymer_11 for details.
<x-pizza>
^^^^^^^^^
Build completed successfully
Why that counts as a successful build, I could not say, but it definitely needs a little something extra. My x_pizza.dart backing code had been:
import 'package:polymer/polymer.dart';
import 'dart:convert';
import 'dart:async';

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // Super cool pizza stuff here...
}
To make this work with no-link-tag importing, I have to annotate the code as a library using @HtmlImport—the @HtmlImport annotation effectively taking the place of the old link-tag importing:
@HtmlImport('x-pizza.html')
library x_pizza;

import 'package:polymer/polymer.dart';
import 'dart:convert';
import 'dart:async';

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
}
The old link-tag importing still works if one prefers that, but this definitely cuts down on the number of lines required in the containing web page.

For this particular code, I am not quite done. Everything compiles and runs without error, but the <x-pizza> element no longer displays any UI. There are no warnings in the browser console, but I do see this message:
No elements registered in a while, but still waiting on 1 elements to be registered. Check that you have a class with an @CustomTag annotation for each of the following tags: 'x-pizza-toppings'
The problem here is that my <x-pizza> uses the <x-pizza-toppings> Polymer element for adding toppings (first half, second half, and whole) to the pizza. This error is telling me that I must also adapt it to the new approach.

I find this a strange error because I am still link-tag importing this child element from the <x-pizza> template:
<link rel="import" href="x-pizza-toppings.html">
<polymer-element name="x-pizza">
  <!-- Pizza html template stuff here.. -->
</polymer-element>
This template definition for <x-pizza-toppings> pulls in the backing class via the usual script tag:
<link rel="import" href="../../../packages/polymer/polymer.html">
<polymer-element name="x-pizza-toppings">
  <!-- Pizza topping html template stuff here.. -->
  <script type="application/dart" src="x_pizza_toppings.dart"></script>
</polymer-element>
And in that backing class is the @CustomTag that Polymer is complaining about:
import 'package:polymer/polymer.dart';

@CustomTag('x-pizza-toppings')
class XPizzaToppings extends PolymerElement {
  // Pizza topping code...
}
I am able to get this working, but then things get really strange...

To get it working, I convert <x-pizza-toppings> to the new no-link-tag style class definition:
@HtmlImport('x-pizza-toppings.html')
library x_pizza_toppings;

import 'package:polymer/polymer.dart';

@CustomTag('x-pizza-toppings')
  // Pizza topping code here...
}
In the code for the XPizza backing class, I now have to import (the Dart kind, not the link-tag kind) the x_pizza_toppings.dart code:
@HtmlImport('x-pizza.html')
library x_pizza;

import 'package:polymer/polymer.dart';
import 'dart:convert';
import 'dart:async';
import 'x_pizza_toppings.dart';

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  //  Pizza stuff here...
}
I also have to work back through the HTML template definitions in both x-pizza.html and x-pizza-toppings.html to remove the old link imports and the <script> tags that reference the backing class. Removing the <script> tags makes sense—the backing class is now responsible for pulling the template, not the other way around. So that is not too strange.

What is strange here is that I can no longer reproduce the original “waiting on 1 elements to be registered” error condition. If it revert my changes completely and retrace my steps, things either work or fail in different (more easily understood) ways. It feels like some bad code got cached somewhere (in the .pub directory?) during my initial pass at this, but can no longer be produced. That or I did something crazy the first pass through.

I am uncomfortable with this—it feels like I need to be quicker to manually delete .pub when I start doing this kind of thing. At the same time, I shouldn't have to do anything of the sort. For now, I chalk it up to pre-release glitches and (more likely) a dumb mistake that I can no longer remember.

In the end, I like the new approach. The waiting-for-registration problem leaves me a little unsettled, so I am not quite ready to adopt it as the approach in Patterns in Polymer. But it is probably worth a mention somewhere.


Day #6

Sunday, March 22, 2015

Scheduled Polymer and Angular (Dart) Tests


I continue to evaluate Patterns in Polymer against the latest pre-release version of Polymer.dart (0.16). Last night I verified that Angular.dart solution from the book still works. Previously, I verified that simple testing still works. Tonight, I hope to verify that testing with Angular.dart works—thus verifying that more complex testing is still working with Polymer.dart 0.16.

I'm fond of scheduled_test for testing asynchronous Dart code. It sits right on top of the standard unittest, so most of the usual methods and matchers work. What it adds to unittest is nicer support for scheduling otherwise asynchronous actions in order, making tests for this type of code deterministic.

The scheduled_test version of the simple, Polymer 0.16 test from the other day is:
library angular_test;

import 'package:scheduled_test/scheduled_test.dart';
import 'package:unittest/html_config.dart';
import 'package:polymer/polymer.dart';

main() {
  useHtmlConfiguration();
  initPolymer();

  var _el;
  group("<x-pizza>", (){
    setUp((){
      schedule(()=> Polymer.onReady);

      schedule((){
        _el = createElement('<x-pizza></x-pizza>');
        document.body.append(_el);
      });

      currentSchedule.onComplete.schedule(() => _el.remove());
    });

    test('has a shadow root', (){
      schedule(() =>
        expect(query('x-pizza').shadowRoot, isNotNull)
      );
    });
  });
}
I import the testing, test output formatter, and Polymer packages. In the main() entry point, I use the test formatter and initialize Polymer, then I start my test. In setUp(), I have two schedules. The first returns the Polymer.onReady future. The scheduled_test library will wait until this future completes before running any other schedules. So, in setUp(), the custom <x-pizza> Polymer element will not be added to the test page until Polymer is ready to process it—perfect! The last bit of code in setUp() is the scheduled_test version of a tear-down block—it removes the custom element after the test is executed.

As for the test itself, it schedules the expectation that the shadow DOM of the custom element is present—a very basic sanity check that Polymer is working. It is important that this test wrap a schedule. Without it, this test will run before the setUp() schedules, which are placed into a queue. In other words, omitting the test schedule would evaluate expectation before Polymer was ready and before the element had been added to the page.

That test passed, but what about a Polymer test on an Angular page? The Angular.dart package contains a lot of testing infrastructure, but I find it hard to use for application testing. It seems of more benefit for testing Angular itself, which is its purpose after all.

So I create an entire application in my test:
    group("angular", (){
      XPizzaComponent xPizza;

      setUp((){
        schedule((){
          var app = new PizzaStoreApp()
            ..bind(NodeBindDirective)
            ..bind(RouteInitializerFn, toValue: storeRouteInitializer);

          applicationFactory()
            .addModule(app)
            .rootContextType(Pizza)
            .run();
        });
        // More schedules here...
      });
      // Tests here...
    });
The PizzaStoreApp() and router are dumbed-down versions of the ones used in the actual application. It is probably a mistake on my part, but my router actually points to a separate page in my test directory:
void storeRouteInitializer(Router router, RouteViewFactory views) {
  views.configure({
    'custom-pizza': ngRoute(
        defaultRoute: true,
        view: 'custom.html'
      )
  });
}
I am purposely trying to test that my Polymer element works when used in an Angular route, so I need something like this, but I am unsure if this is the best way to go about doing so. If nothing else, this requires me to wait for the custom.html partial to be loaded and inserted into the view. And I have no idea how to do this in Angular.dart, so I cheat:
    group("angular", (){
      setUp((){
        schedule((){ /* Schedule Angular app setup */ });o
        schedule((){
          var _completer = new Completer();
          new Timer(new Duration(milliseconds: 50), ()=> _completer.complete());
          return _completer.future;
        });
        // ...
      });
    });
If this were AngularJS, I could wait for a $viewContentLoaded event before completing this schedule. Unfortunately, there is no Anguar.dart equivalent for this event. So I am forced to wait some arbitrary amount of time. Ew.

Ugliness aside, my tests still work. I can verify that an Angular value bound to my Polymer element is updated in response to a change in the Polymer element:
      test('can double-bind to Polymer properties', (){
        schedule(()=> xPizza.addWholeTopping('green peppers'));
        schedule((){
          var angular_el = document.query('pre');
          expect(
            angular_el.text,
            contains('"whole":["green peppers"]')
          );
        });
      });
The xPizza object is a Page Object that makes testing interaction a little clearer in my tests. In the end, these (and other) tests pass with the latest Polymer.dart:
PASS
1       PASS
Expectation: <x-pizza> has a shadow root .

2       PASS
Expectation: <x-pizza> defaults to a blank pizza .

3       PASS
Expectation: <x-pizza> adding toppings updates the pizza state with new toppings .

4       PASS
Expectation: <x-pizza> angular can double-bind to Polymer properties .

All 4 tests passed
I really ought to get a better handle on testing Angular applications, but the hack that I have been using all along in Patterns in Polymer continues to work.



Day #5

Saturday, March 21, 2015

Bleeding Edge Polymer.dart and Angular.dart


When last I checked, the state of Polymer.dart and Angular.dart was pretty rough. I had to override multiple dependencies just to get the two to install side-by-side. At the time, that seemed to work, but hardly seemed a long-term solution. Let's see if the situation has improved.

I continue to work with the latest development release version of Dart (the newest Polymer.dart requires it):
$ dart --version
Dart VM version: 1.9.0-dev.10.12 (Fri Mar 20 06:26:15 2015) on "linux_x64"
In the angular example code from Patterns in Polymer, I start with optimism in my heart. I comment out the old dependency overrides in my pubspec.yaml package file:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind:
    git: https://github.com/eee-c/angular_node_bind.git
# dependency_overrides:
#   args: any
#   code_transformers: any
#   html5lib: any
#   observe: any
dev_dependencies:
  scheduled_test: any
transformers:
- angular:
    html_files:
      - web/partials/custom.html
- polymer:
    entry_points:
      - web/index.html
      - test/index.html
And then I attempt to upgrade to the latest, compatible versions:
$ pub upgrade   
Resolving dependencies... (38.5s) 
And it just stops there. Well, it does not stop—the CPU is pegged and the fan on my laptop bolts into high gear.

Some time later, pub gives up the ghost:
Connection closed before full header was received
So it seems that I must give Dart Pub a gentle nudge at minimum. I start by explicitly requiring the latest Polymer:
name: angular_example
dependencies:
  angular: any
  polymer: ">=0.16.0"
  angular_node_bind:
    git: https://github.com/eee-c/angular_node_bind.git
# ...
Since this is in support of a Polymer book, it seems more appropriate to get the latest Polymer library working with a possibly older version of Angular. But even that fails. I still timeout when trying to pub upgrade. It would appear that a gentle nudge is insufficient. Time for a good firm shove...

I check the generated pubspec.lock of a Polymer only project and an Angular-old project. Comparing the two, I find that Polymer is using a newer version of the observe package and a new version of the collection package.

In the pubspec.lock for the Polymer-only project, I have:
# ...
  collection:
    description: collection
    source: hosted
    version: "1.1.0"
# ...
  observe:
    description: observe
    source: hosted
    version: "0.13.0+1"
# ...
In the pubspec.lock for the Angular-only project, I have:
# ...
  collection:
    description: collection
    source: hosted
    version: "0.9.4"
# ...
  observe:
    description: observe
    source: hosted
    version: "0.12.2+1"
# ....
In both cases, I opt for Polymer to win (again because I am more interested in evaluating Polymer). So I update my Polymer-Angular example application to include some dependency overrides:
name: angular_example
dependencies:
  angular: any
  polymer: ">=0.16.0"
  angular_node_bind:
    git: https://github.com/eee-c/angular_node_bind.git
dependency_overrides:
  collection: '>=0.9.1 <1.2.0'
  observe: '>=0.12.0 <0.14.0'
# ...
That is less than ideal, but I console myself with two rationalizations. First, this halves the number of dependency overrides that I previously had to use. Second, I am using a pre-release version of Polymer. So I do not look too closely and claim progress.

Especially since a pub upgrade now completes successfully:
$ pub upgrade
Resolving dependencies... (11.7s)
  ...
  angular 1.1.0
  angular_node_bind 0.2.0 from git https://github.com/eee-c/angular_node_bind.git at d7b694
  ...
! collection 1.1.0 (overridden)
  ...
> di 3.3.4 (was 3.3.3)
...
! observe 0.13.0+1 (overridden)
  ...
> polymer 0.16.0+6 (was 0.15.5+4)
  ...
Warning: You are using these overridden dependencies:
! collection 1.1.0
! observe 0.13.0+1
Changed 14 dependencies! 
...
Best of all, the Angular and Polymer code still double binds the shared attributes. Angular binds the <x-pizza> element's value attribute to a local variable of its own, which it also binds in a <textarea>:



When I update the Polymer element, adding sausage to the second half of my <x-pizza> Polymer element, Angular sees the attribute change and binds the new value into the <textarea>:



Similarly, when I manually edit the JSON in the <textarea>, the <x-pizza> Polymer element is updated:



Yay!

I close by noting that a recent commit to Angular.dart relaxed the observe package sufficiently so that overrides will no longer be needed. At that point, only an override of the collection dependency will stand between Angular.dart and Polymer.dart in the same project. Exciting!

Day #4