Thursday, May 22, 2014

Polymer Elements Loading SVG in Polymer, uh… Custom Elements?


It occurs to me that, if I have a bunch of static files to load in a custom Polymer element, then perhaps some of the extended Polymer family may be of some use.

I am continuing my efforts to understand how best to build and package a Polymer custom element that includes SVG components. Specifically, the <x-pizza> pizza building element:



My initial attempts at loading SVG files with mundane things like <image> tags went well until I needed to animate those files. I already have a handle on loading a single SVG file with HttpRequest. I think I can probably do the same for the remaining assets, but it occurs to me that I need to do this in JavaScript and Dart, which complicates things.

The driving force behind this is producing screencasts for the “Extras” version of Patterns in Polymer. I plan on producing short screencasts in both JavaScript and Dart (since the book comes in both flavors). Herein lies the complication. XHR coding in JavaScript is pretty awful without a separate library. And if I'm pulling in libraries, why not just stick with Polymer? Or, in this case the <polymer-ajax> custom element that comes in Polymer Elements.

To explore this idea, I stick with Dart for now and start by updating my application's pubspec.yaml to depend on Polymer Elements:
name: svg_example
dependencies:
  polymer: any
  polymer_elements: any
dev_dependencies:
  unittest: any
transformers:
- polymer:
    entry_points: web/index.html
A quick pub update and I am ready to rock.

And rock I do. Starting with the <x-pizza.html> template, in which I import the Polymer Elements' <polymer-ajax> tag and put it to use loading the various SVG files:
<link rel="import" 
      href="../../../packages/polymer_elements/polymer_ajax/polymer_ajax.html">
<polymer-element name="x-pizza">
  <template>
    <polymer-ajax
       id="pizza.svg"
       url="/packages/svg_example/images/pizza.svg"
       on-polymer-response="{{responseReceived}}"></polymer-ajax>
    <polymer-ajax
       id="pepperoni.svg"
       url="/packages/svg_example/images/pepperoni.svg"
       on-polymer-response="{{responseReceived}}"></polymer-ajax>
    <!-- ... -->
  </template>
  <script type="application/dart" src="x_pizza.dart"></script>
</polymer-element>
Inside my own custom element, I follow the convention of using the SVG filename for the tag ID. This should make it easy to decipher which element has loaded when the responseReceived() method is invoked as each of those SVG files is loaded in turn. Speaking of responseReceived(), I define that as:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
  Map svgContent = {};
  responseReceived(e, detail, node){
    svgContent[node.id] = detail['response'];
    if (node.id == 'pizza.svg') _updateGraphic();
  }
  // ...
}
With each of the SVG file contents now available in the svgContent map, I can use them in places like _updateGraphic():
  _updateGraphic() {
    svg = new SvgElement.svg(svgContent['pizza.svg']);
    $['pizza']
      ..children.clear()
      ..append(svg);

    _addWholeToppings();
    _addFirstHalfToppings();
    _addSecondHalfToppings();
  }
That works quite nicely. It would be nice if the Dart version of <polymer-ajax> exposed a Future instead of relying on the responseReceived() bound methods. Even so, this is a relatively clean solution for SVG file loading in my custom Polymer element. Up tomorrow: I see if I can exploit this approach to animate some of the individual SVG files.



Day #71

No comments:

Post a Comment