Polymer lets you choose between Shadow DOM, which comes natively from the browser, or Shady DOM, which is a polyfill for browsers that do not yet support Shadow DOM natively.
But the Shady DOM is not a functionally complete polyfill for Shadow DOM. It does not provide as much name hiding or encapsulation as Shadow DOM.
Under Shady DOM I was starting to get some name conflicts. So, since my app only targets Chrome, I decided to switch from Shady DOM to Shadow DOM for the more powerful encapsulation. Polymer allows you to do this easily by just setting a flag. No real code changes are needed.
This got rid of my naming conflicts but it created a whole slew of other problems. Why? Because some of my web components were used in HTML forms, the old fashioned type of form that gets submitted with a submit button (not AJAX). It turns out that any form elements (input, select, textarea) inside of a web component are not submitted.
That's a big deal. Let me repeat:
Any form elements inside of a web component are not submitted to the server.
Here is an example:
In this case, date-range is my custom web component. The date-range component consists of two html inputs internally:
From: <input name="d1">
To: <input name="d2">
With Shady DOM, the form submitted its data like this:
With Shadow DOM, here is what gets submitted:
In other words, nothing.
This actually makes a little bit of sense when you consider that the whole point of Shadow DOM is encapsulation, i.e. keeping internal stuff hidden.
So how do you get around this problem?
Here are three possible work arounds:
2. Use an additional hidden text input (or two in this case) which live outside of the web component (but still inside the form). Then keep the web component in sync with the text input by using a change listener. This works but it's ugly and undoes the elegance of using web components in the first place.
3. Polymer offers a Polymer-specific solution called iron-form. This solution has some problems. For one, it does not emulate native form submission very well. In a normal html form, when you submit, the current page is automatically replaced by whatever is returned by the action URL. This doesn't seem to happen with iron-form. Second. It only allows your component to contribute a single value to the submitted data. In the above mentioned date-range example, I would like two values to be submitted like this:
This can be worked around by doing something like this:
But this adds extra work on the server-side.
In short, using iron-form requires a major rewrite to your application.
A Better Solution
If you haven't noticed, none of these work arounds seems all that satisfactory. The root problem I think is this: the creators of the web component specs did not really consider how web components would work in the context of html forms.
Here is what I propose for a more sound solution: The web component spec must provide a way to expose form submit fields similar to the way you currently are allowed to expose properties. Thus a web component's API would consists of 4 things:
- Form Submit Fields
I don't know how common it is to use the traditional technique of "form submit" to submit data to the server but at the very least it's common in legacy web apps. If you want to take an app like that and gradually start replacing some of the form elements (inputs, select, etc) with custom web components then you are in for a whole world of hurt.
This is a use-case that clearly was not considered in the design of web components or polymer. Especially when using Shadow DOM.