Tuesday, February 4, 2014

Submitting Polymer Data in Plain Old Forms


If an <input> element embedded in a Polymer which is wrapped by a <form> cannot be reliably submitted by that <form>, what are my options for submitting Polymer form data?

More concretely, what are my options for submitting the value in the “Embedded Polymer field” along with the remainder of the form in the following?



I am pretty sure that I need some variation the mutation observer from the other night, but, before I can get to that, I need to ensure that the form elements inside my Polymer are not submitted on the off chance that the current browser and/or version of Polymer accidentally tries to use them. That is a simple matter of not naming the <input> tag from last night. So I remove name="embedded_polymer_param" in the following:
<polymer-element name="hello-you">
  <template>
    <h2>Hello {{your_name}}</h2>
    <p>
      <input type=text value="{{your_name}}" placeholder="Embedded Polymer field">
      <button on-click="{{feelingLucky}}">Colorize!</button>
    </p>
    <p class=help-block>
      <content></content>
    </p>
  </template>
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
Now that I think about it, if I embed an instance of this <hello-you> Polymer inside a <form> tag, the Polymer's <button> could very well submit the form. I could (and probably should) add type="button" to override the default of submit, but first, I realize that I have no idea what arguments would be sent to the on-click Polymer event handler.

It turns out to be a plain-old event (well, a Polymer wrapped event, but still an event). So I can prevent the click with the usual preventDefault and a return false:
@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  // ...
  feelingLucky(e) {
    //  Pick a random color here...
    e.preventDefault();
    return false;
  }
}
(the same thing works in the JavaScript equivalent)

That is good information to have for my Polymer toolbelt, but it seems safest to use non-form interacting form elements: inputs without names and buttons with type="button":
      <input value="{{your_name}}" placeholder="Embedded Polymer field">
      <button type="button" on-click="{{feelingLucky}}">Colorize!</button>
To actually get that data into the form, I add a mutation observer from the main() entry point:
main() {
  initPolymer().run((){
    var observer = new MutationObserver((mutations, _) {
      var el = query('#your_name');
      mutations.forEach((mutation) {
        if (mutation.attributeName == 'your_name') {
          el.value = mutation.target.attributes[mutation.attributeName];
        }
      });
    });
    observer.observe(query('hello-you'), attributes: true);
  });
}
There is nothing too new in there. I find a hidden input with id=your_name in the form. Whenever the mutation observer notices a change to the your_name attribute on the Polymer, it updates the value of that same hidden input.

This will not work for arbitrary form elements inside the Polymer. If there were additional <input> tags inside the Polymer, the mutation observer approach would not work—at least not without working pretty hard to dig down into the shadow DOM of the Polymer. But really, this seems OK. Unless the Polymer publishes a value as an attribute, the external page context should almost certainly treat unpublished values as private. In such cases, either the Polymer would have to change to expose another attribute or the developer using the Polymer would have to rethink the best use of the Polymer.



Day #1,017

No comments:

Post a Comment