Saturday, August 16, 2014

Don't Mix Browser Events in Paper (Polymer)


I could play with Paper Elements and the benefits of adhering to Material Design all day:


But the gods of my chain and Jerry Seinfeld would frown on that, so...

Up today, I worry about event propagation in paper elements. Specifically, when I add a pizza topping to an <x-pizza>, the event should not trigger the pizza toppings card to transition back to its initial “chip” state:


The ingredients are core-item elements whose on-click attribute points to the add() method on the backing class:
<polymer-element name="x-pizza-toppings" attributes="ingredients name">
  <template>
    <!-- ... -->
    <template repeat="{{ingredient in ingredients}}">
      <core-item
         scale-up
         id="{{class_name(ingredient)}}"
         icon="add-circle"
         label="{{ingredient}}"
         on-click="{{add}}">
      </core-item>
    </template>
  </template>
  <script src="x_pizza_toppings.js"></script>
</polymer-element>
So my first thought is to prevent event propagation in every conceivable way:
Polymer('x-pizza-toppings', {
  // ...
  add: function(e) {
    this.model.push(e.target.label);

    console.log('stop the events!')
    e.preventDefault();
    e.stopPropagation();
    e.stopImmediatePropagation();
    return false;
  },
  // ...
});
Unfortunately, it seems that my first instinct is incorrect. Actually, it turns out to be just a little off.

The transition between chip and card (and back) are both listening for tap events, not click events:
    <core-animated-pages selected="{{toppingsListOrDetails}}" transitions="hero-transition">
      <section>
        <div class="chip-container"
             hero
             on-tap="{{transitionTopping}}">
          <!-- Chips go here... -->
        </div>
      </section>
      <section>
        <div class="card" layout horizontal
             hero-id="{{selectedTopping}}"
             hero
             on-tap="{{transitionTopping}}">
          <!-- Chips go here... -->
        </div>
      </section>
    </core-animated-pages>
Tap events are one the Polymer normalized events, which aid in cross-platform and cross-device development. The tap event work for both mouse and finger, firing when the pointer goes down and up again.

So the fix is to adopt a consistent approach to events:
    <template repeat="{{ingredient in ingredients}}">
      <core-item
         scale-up
         id="{{class_name(ingredient)}}"
         icon="add-circle"
         label="{{ingredient}}"
         on-tap="{{add}}">
      </core-item>
    </template>
And then, I can reduce the event propagation to simply stopPropagation():
Polymer('x-pizza-toppings', {
  // ...
  add: function(e) {
    this.model.push(e.target.label);
    e.stopPropagation();
  },
  // ...
});
With that, I can add toppings to my heart's content:


It only makes sense to be consistent with events. This is, after all, the point of normalizing some of the events in Polymer. Hopefully I will take this lesson to heart in the future.


Day #154

No comments:

Post a Comment