Thursday, December 4, 2014

One Weird Trick to Getting Polymer.dart Packages Working


Yeah, sorry about the title… I couldn't resist.

I wound up with the strangest little bug in some Polymer.dart code yesterday that I just could not figure out. I had originally started investigating the "is" attribute in Polymer, but quickly realized that I could not use it the way I wanted.

In the end, the best way to implement inheritance in Polymer seems to be with the facilities built into the language. In the case of Dart, that mean regular inheritance with the class extends modifier or via mixins. Both work pretty darn well, unless…

Unless you are trying to extend one Polymer class with another through a Dart Pub package. Consider last night's code:
import 'package:polymer/polymer.dart';
// import 'a_form_input.dart';
import 'package:a-form-input/a_form_input.dart';

@CustomTag('x-pizza')
class XPizza extends AFormInput {
  // ...
}
The a_form_input.dart files in the local directory and in the package are identical:
$ diff -s a_form_input.dart \
  ~/repos/a-form-input-dart/lib/a_form_input.dart
Files a_form_input.dart and 
  /home/chris/repos/a-form-input-dart/lib/a_form_input.dart
  are identical
But when I use the local version, the attributeChanged() in from a_form_input.dart gets invoked:
library a_form_input;

import 'package:polymer/polymer.dart';

@CustomTag('a-form-input')
class AFormInput extends PolymerElement {
  @PublishedProperty(reflect: true)
  String name;
  @PublishedProperty(reflect: true)
  String value;
  AFormInput.created(): super.created();
  // ...
  void attributeChanged(String name, String oldValue, String newValue) {
    print('$name: $oldValue → $newValue');
    if (name == 'name') lightInput.name = newValue;
    if (name == 'value') lightInput.value = newValue;
  }
}
When I add toppings to the <x-pizza> element, it sets the value property internally, which triggers the callback (including the print statement):
value: First Half: []
Second Half: []
Whole: [] → First Half: [pepperoni]
Second Half: []
Whole: []
Making no other change to the <x-pizza> element other that importing the package version of a_form_input.dart results in… nothing. There are no errors, there is no print statement to be seen in the console and the element is certainly not behaving like "a-form-element", which is the whole point of moving this out into a package.

The answer to this little puzzle turns out to be pubspec.yaml. If I add a Polymer transformer to the a-form-element package, then everything magically works:
name: a-form-input
author: "Chris Strom "
description: "Polymer element that mimics a native <form> input."
dependencies:
  polymer: any
transformers:
- polymer
Being somewhat familiar with the idiosyncrasies of Polymer.dart, it did not take long for me to try this. Even so, this fix seems far from obvious. Even now that I know the solution, I cannot rationalize an argument for why this is necessary. I would expect the transformer that is already in my application's pubspec.yaml would apply to any elements that are imported:
name: form_example
dependencies:
  polymer: any
  a-form-input:
    path: /home/chris/repos/a-form-input-dart
transformers:
- polymer:
    entry_points:
      - web/index.html
      - test/index.html
Even if that did not apply to vanilla elements, surely it would apply to elements that only serve as the base class for an element that is being defined in the application, right? I already know the answer, but the "why" of the no answer remains something of a mystery.

One mystery solved (how to make it work) is enough for today. Tomorrow, I may poke through the transformer again to solve the mystery of why it works.


Day #14

2 comments:

  1. This is a known issue http://dartbug.com/20673. The comments on the issue contain some background information.
    The main problems seems to be that transformers can't modify code from other packages.
    We stumbled upon this problem when we started working on polymer_ui_elements which imports polymer_elements about a year ago - time just flies by ...

    ReplyDelete
    Replies
    1. Good to know that I'm not the only one thoroughly confused by that behavior!

      Delete