Sunday, August 10, 2014

Polymer Core-Item


I am pleased with my Paper element based implementation of the <x-pizza> Polymer element:



The tabs (paper-tabs) are pretty and thoughtfully animated. The icons were easy enough (core-icons). But as easy as these were, I have the feeling that I am missing out on some core or paper goodness.

Up tonight, I think that I can improve upon the HTML template for the topping list that might even help with some of the backing code. The HTML in the <x-pizza-toppings> template that makes the ingredient list looks like:
    <ul class="toppings">
      <template repeat="{{ingredient in ingredients}}">
        <li id="{{class_name(ingredient)}}" on-click="{{add}}">
          <core-icon icon="add-circle"></core-icon>
          {{ingredient}}
        </li>
      </template>
    </ul>
While skimming the documentation, I had initially thought that the list might be better done as a core-selector. Re-reading the documentation, that seems more like it would apply to a radio-button style list—where one item is active in a list. That still might be useful here, but it also might be too much of an impedance mismatch.

So instead, I start by trying to improve on the list with core-item. After installing with Bower:
$ bower install Polymer/core-item --save
I add it to the <x-pizza-toppings> element's import list:
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/core-icons/core-icons.html">
<link rel="import" href="../bower_components/core-item/core-item.html">
<polymer-element name="x-pizza-toppings" attributes="ingredients name">
  <template>
    <!-- ... -->
  </template>
  <script src="x_pizza_toppings.js"></script>
</polymer-element>
With that, I can replace my LI tags with a core-item implementation:
    <template repeat="{{ingredient in ingredients}}">
      <core-item
         id="{{class_name(ingredient)}}"
         icon="add-circle"
         label="{{ingredient}}"
         on-click="{{add}}">
      </core-item>
    </template>
That feels cleaner. And the backing code becomes simpler for it. With the previous implementation, I had to check the click event to see if the label, the icon, or the LI itself was being clicked:
Polymer('x-pizza-toppings', {
  // ...
  add: function(e) {
    var el = e.target;
    if (el.tagName != 'LI') el = el.parentElement;
    var ingredient = el.textContent.replace(/^\s*/, '').replace(/\s*$/, '');
    this.model.push(ingredient);
  },
  // ...
});
Now that all of that goes into the <core-item> element, the backing code becomes much simpler:
Polymer('x-pizza-toppings', {
  // ...
  add: function(e) {
    this.model.push(e.target.label);
  },
  // ...
});
This is an unexpected benefit of working with core elements—and really any Polymer elements. The code that consumes and works with these elements is far simpler because the elements themselves are far simpler.

Similarly, the styling of the elements gets simpler as well. Previously, I had to worry about styling the UL in addition to the list items:
      ul.toppings {
        list-style-type: none;
        margin: 0;
        padding: 0px 1em;
      }

      ul.toppings li {
        cursor: pointer;
        margin-bottom: 0.5em;
      }

      ul.toppings li:hover core-icon {
        fill: #fb8c00;
      }
Now I only need to worry about the core-items:
      core-item {
        cursor: pointer;
        margin-left: 0.5em;
      }

      core-item:hover::shadow core-icon {
        fill: #fb8c00;
      }
The only trick in there is the ::shadow selector option. Because core-item places the icon in its shadow DOM, the only way that <x-pizza-toppings> can style it is via the shadow modifier. I also tried the :hover pseudo-selector after ::shadow, but that has no effect. The ::shadow has to come last.

Note: I try removing the core-icons import from <x-pizza-toppings>, but this prevents <core-item> from using the element. All that is left is a blank space where the add icon used to be. In other words, the core-icons import is required to use them with core-item. I suppose that is understandable—items might not have an icon or might use only images (via the src attribute). Still, something of which to be aware when working with these things.

Amazingly, last night's tests pass throughout this refactoring. The Page Object that manipulates the Polymer element relied on class names instead of tag and content values.

The end result of tonight's effort: 21 deletions vs. 14 insertions. Plus a whole lot easier to read code and HTML. That is a nicer than expected win. There may be something to these Core elements after all!


Day #148

No comments:

Post a Comment