Sunday, February 9, 2014

Extending Built-in Elements with Polymer


I just completed the writeup for using custom Polymer elements in plain-old HTML forms in Patterns in Polymer. Now I wonder if the entire premise was wrong. That's par for the course for me, but thankfully I have these little research spurts to allow me to investigate.

The solution currently presented in Patterns in Polymer is based on using mutation observers to watch a Polymer attribute, updating a hidden HTML form element accordingly. That works fine and really is not too much code. Still, I wonder if there is a better solution. Specifically, what if the Polymer inherits from InputElement? Would the Polymer work directly inside a form like any other element?

In the Dart version of a very simple <hello-you>, I update the polymer-element declaration to extend the input element:
<polymer-element name="hello-you" extends="input">
  <template>
    <!-- ... -->
    <input type=text name="embedded_polymer_param" value="{{value}}">
    <!-- ... -->
  </template>
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
Then, in the backing hello_you.dart, I change the class to extend InputElement and mixin both Polymer and Observable:
@CustomTag('hello-you')
class HelloYou extends InputElement with Polymer, Observable {
  @published String value;
  HelloYou.created() : super.created() {
    polymerCreated();
  }
  // ...
}
Since this no longer extends PolymerElement directly, I also have to manually invoke polymerCreated() when the element is created.

With that, I am ready to use a “hello-you” version of an <input>. Since this is extending a built-in field, I have to use the regular HTML name and tack on is="hello-you" to use the extended version:
      <form action="test" method="post">
        <input type=hidden id=your_name>
        <h1>Plain-old Form</h1>
        <input type=text name="plain_old_param">
        <input name="polymer_value" is="hello-you">
          <input type=text name="distributed_param">
        </input>
        <button>Play</button>
      </form>
And that works. Kind of.

When I submit the form, I see the value from the Polymer extended <input> among the form parameters:
plain_old_param:
polymer_value:asdf
distributed_param:
The problem is the use of the <input> element. It does not accept child elements, which breaks trying to project content into the Polymer:



The element with the “Projected” placeholder text ought to be rendered in the Polymer, not to the side. There is also the weird styling on the <input>. Well, not too weird, this is an <input> after all. Still, it would have to be addressed if I wanted to push this further.

The main reason that I would be unlikely to use this approach over mutation observing is that it makes it hard to work with multiple values from the Polymer. Also, unless I am mistaken, extending browser elements does not work at all in the JavaScript version of Polymer. The example given in the documentation extends another Polymer, not a standard browser element. When I tried it in JavaScript:
<polymer-element name="hello-you" extends="input" attributes="value">
Then the element was ignored completely.

Phew! So a complete chapter rewrite is not necessary, which is always nice. I will likely add a sentence in the JavaScript version of Patterns in Polymer about not being able to extend built-in elements. It may be a paragraph about the limitations of doing so in the Dart version. But either way, the current mutation observer approach seems solid.


Day #1,022

No comments:

Post a Comment