WICG / webcomponents

Web Components specifications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Need callback for form submit data

hayatoito opened this issue · comments

Title: [Custom]: Need callback for form submit data (bugzilla: 24603)

Migrated from: https://www.w3.org/Bugs/Public/show_bug.cgi?id=24603


comment: 0
comment_url: https://www.w3.org/Bugs/Public/show_bug.cgi?id=24603#c0
Erik Arvidsson wrote on 2014-02-10 16:09:15 +0000.

Right now there is no way to have custom elements include data in form submissions. We should add another callback for this

I believe we need to add a callback that is called before the submit event.

Strawman:

document.registerElement('input', {
prototype: {
proto: HTMLElement.prototype,
beforeSubmitCallback: function() {
switch (this.type) {
case 'checkbox';
if (this.checked)
return this.value;
return undefined;
...
}
}
}
});

Basically, the contract is that the return value of the callback is used a the form value. If undefined is returned nothing is serialized.

This is of course a bit too simplistic but it might be enough to get started.

Things to keep in mind:

  • Radio buttons need to check outside itself
  • input[type=file]. Return Blob|File|data url?
  • input[multiple]. Array of values?

comment: 1
comment_url: https://www.w3.org/Bugs/Public/show_bug.cgi?id=24603#c1
Boris Zbarsky wrote on 2014-02-10 16:20:44 +0000.

If is a subclass of , why can't it just set the .value to the thing it wants to submit?

If it's not a subclass of , you have other problems too, like form validation not knowing anything about it and so forth...


comment: 2
comment_url: https://www.w3.org/Bugs/Public/show_bug.cgi?id=24603#c2
Erik Arvidsson wrote on 2014-02-10 16:30:48 +0000.

I agree it might be simpler to just subclass input but at some point we should explain how input is implemented too. That also includes explaining form validation and requestAutoComplete and probably tons of other things.


comment: 3
comment_url: https://www.w3.org/Bugs/Public/show_bug.cgi?id=24603#c3
Boris Zbarsky wrote on 2014-02-10 16:37:53 +0000.

Sure. If we do that we need to make all the relevant algorithms robust to script randomly doing unexpected things. E.g. getting the value from an input can change the values of other inputs if it's done in arbitrary script....

The way input is implemented in practice, in ES terms, is that it has private slot (closure variable, weakmap entry, whatever) that stores a value string, and various form operations can retrieve that string without running any untrusted (from the point of view of the form) script in the process. Whatever setup we come up with for explaining input would be best if it preserved those invariants....


comment: 4
comment_url: https://www.w3.org/Bugs/Public/show_bug.cgi?id=24603#c4
Anne wrote on 2014-02-12 18:16:49 +0000.

What about form association and label association? It seems you need hooks for those too.


comment: 5
comment_url: https://www.w3.org/Bugs/Public/show_bug.cgi?id=24603#c5
Dimitri Glazkov wrote on 2014-05-08 20:51:20 +0000.

There's another proposal here: http://lists.w3.org/Archives/Public/public-webapps/2014JanMar/0448.html

Custom elements F2F: we would like this! Previously, https://lists.w3.org/Archives/Public/public-webapps/2014JanMar/0448.html

Current thinking is:

  • Probably this should be FormData based
  • We should refactor the HTML spec to have "form data steps" per element to make the design a bit clearer (https://html.spec.whatwg.org/multipage/forms.html#constructing-form-data-set)
  • Ideally we'd like to have a setValue/getValue pair so that this could also handle form restoration when doing back/forward/reopening the browser/etc.
  • We're not sure how to best guide authors to having unique names for their fields. (E.g. using the name attribute or some derivative of it like type=image does with name + "x"/"y".)

The last point is why we decided to use string & JSON for the value so that we can use name attribute of the element as done for any builtin element.

Using FormData is slightly nicer because it can contain multiple name-value pairs for when a compound element such as a credit-card component needs to submit multiple data fields. But letting users of a component specify the value might be more valuable than each value being submitted as a separate name-value pair. Also, user of such a component can always split the JSON into individual name-value pairs during submit event although having to parse the JSON there is kind of gross.

Yet anther option is to let users of component specify names for each value the component supplies but this is very different from the way builtin elements work so probably not a good idea.

@domenic if it is FormData based it would still submit normally when pressing the submit button, correct?

Awesome. But since you tagged it v2: If it does come into the specs it will probably be a year or more, correct? Since v1 is just releases and not even really implemented anywhere.

No, "v2" is a misnomer. The specs are living and see continuous changes, and implementations implement those changes as they can prioritize them, according to each implementation's individual release cycle. In the issue tracker, "v2" means something roughly like "not part of the initial set of features that we reached consensus on in the January 2016 face to face meeting".

Ahh, I see. Thanks for the clarification. Where can I best track the progress of this, to see if/when it will come?

Watching this issue is the best way to get updated on spec progress. After that, the procedure is specific to each browser; places like the various browser status trackers or bug trackers will be where implementation-specific discussion happens. That's kind of outside the scope of this repo though.

Has there been any progress on this?

We want to develop internal components that are project/framework agnostic. Ideally we want to be able to support Angular, React, and MVC patterns. The last one is the sticking point. Several of the components I want to build are basically form elements so I should be able to drop them in a form and submit with no additional code needed.

Is there a workaround to support submitting the component's values with the enclosing form on submit?
What about self-validating components, where the component can prevent form submission if it is in an invalid state?

@PleasantD the only workarounds we've been able to find are to replicate the behaviour of form elements (which requires you to also to replicate <form />) or to not use shadow DOM at all.

Replicating form and input behaviour is robust, and allows you to keep shadow DOM encapsulation, but can work well when implemented correctly and edge-cases are tested. We've documented roughly how to do this here: https://skatejs.gitbooks.io/skatejs/content/docs/recipes/form-behaviour-and-the-shadow-dom.html. This can work with any web component library, not just Skate. The downfall here is that your custom <my-form /> will be tightly coupled to your <my-input /> elements. Your custom input elements won't be able to be dropped into a standard <form /> element, thus not reusable unless your consumers also use your custom form element.

Not using shadow DOM works well for leaf nodes. Luckily, most form elements are leaf nodes. However, it falls short when you want to use the custom element in a library that takes over control of childNodes, like React. I know libraries like Incremental DOM and Preact have since lightened up how they destroy nodes they don't know about, but I'd err on the side of replicating form behaviour until this gets implemented.

You could try and use the is attribute. This would in fact be the most elegant approach, but you'll never be able to remove the polyfill because, at least, Safari will never implement it.

To re-iterate, we're interested in making this proposal polished & implemented in WebKit.

@domenic @annevk Since there is implementer interest, is this ready to be specced? Is somebody already working on this? If nobody is, I can volunteer to try to help but I have never written a spec so I might need a ton of hand holding :(

@marcoscaceres @bkardell who have also expressed interest in working on this.

This is ready to be specced, and I'd be happy to mentor. However, @tkent-google also volunteered to spec as well, so we should coordinate with him.

It would be really ideal to come into the meeting with an explainer that has one preferred API shape, and discusses how it addresses the various things raised in this thread.

Totally happy if @tkent-google takes it. I'm still going with Payment Request, but super interested to see how this progresses (and hopefully we can get the payment stuff to align with whatever happens with forms here).

@notwaldorf That would be great. We don't necessarily need the full spec. We mostly need a concrete API design. I don't think the API should be specific to custom elements although we should make sure form submission API works well with custom elements API.

@rniwa i think @tkent-google is on it, so I'll let him follow up on this :)

I'm happy to work with @tkent-google on an explainer to start (tho I don't have a contact). Seems like the right place to make sure the proposal addresses the right stuff and can explain why? I have notes and emails with discussions and proposals from a few years back that I don't think ever made it into formal stuff.

Another good starting point here would be fixing whatwg/html#1840. Once you figure out the requirements for the existing elements and have refactored them, usually an API will fall out quite nicely of such an effort. I think we generally end up with better proposals when we resolve technical debt first.

FWIW, I don't think form submission is something that should be tied to web component features since it's useful independent of whether a site uses web components or not. So I'd suggest we do the work in whatwg repository instead.

Hi everyone,

Here is an explainer document: https://docs.google.com/document/d/1JO8puctCSpW-ZYGU8lF-h4FWRIDQNDVexzHoOQ2iQmY/edit?usp=sharing

It contains two proposals. One is based on eoconnor@apple's strawman. Another is specific to custom elements. The reason of two proposals is described in the document.

@tkent-google thanks, that looks interesting. One alternative that wasn't considered it seems is making ShadowRoot the extension hook. I personally rather like that approach (it's also planned for accessibility and considered for drawImage() extensibility) as it allows for encapsulating the ability and implementation. I'm kinda hoping we can consider ShadowRoot first when we need an element extension point and only go elsewhere if that doesn't work. (I don't think this would end up being a major change to what you propose.)

Hooking ShadowRoot doesn't sounds reasonable in this case because not all participating custom elements need a shadow tree. For example, making a contenteditable element submittable doesn't need a shadow tree.

class SubmittableRichEdit extends HTMLElement {
  constructor() {
    super();
    setAttribute('contentEditable', true);
  }
  formDataCallback(formData, form) {
    formData.append(getAttribute('name'), this.innerHTML);
  }
  formResetCallback() {
    this.textContent = '';
  }
}

How is it not reasonable to require one to be used? Implementations should be able to detect and optimize if it's used just for extension hooks.

Hi everyone,

FWIW, I really like the proposal A, having an API to make any object participant of a form would be an awesome feature! :)

That being said, in that proposal, the way a custom element is registered as a form participant seems quite cumbersome and complicated. I'm wondering if you have though about a declarative way of doing that. For instance, instead of this quite complicated connectedCallback why can't we use the markup/attributes to express that ? Something like:

<form id="my-form">
<!-- ... -->
  <my-control participant="name-in-the-form-data" />
  <!-- not name="name-in-the-form-data" because of <a name=""> -->
</form>
<!--  and even -->
<another-control participant="another-name" form="my-form" />

the browser would then be responsible for calling addParticipant. Also, with that, we could imagine that you don't need to handle the disabled state in the formDataCallback because the browser would not called it if the element is disabled.

Cheers

We definitely shouldn't use a solution that always requires a custom element to participate in a form submission so we should go with option A. It would be also great if we can split out validation stuff since that would raise a lot of complexity.

Having said that, I really do not want poll model for validity checks. I'd much rather each element set its own validity as it changes.

Proposal A and B in the document are NOT exclusive. My current opinion is that we should provide both.

We should make consensus step by step.

  1. Should we support non-element participants?
    I assume YES. Several people said it's necessary, and there were no objection.
  2. Should we support element-specific features such as participate-by-markup, &lt;label> association, interactive form validation.
  3. If the consensus for the above issue is YES, we need something like Proposal B and need to define the details.
    • customElement.define() and additional callbacks,
    • Hooks on ShadowRoot,
    • introduce dedicated content attribute like participant=,
    • or something

I strongly believe we should not couple form-control-ness to having a shadow DOM; the concepts are largely unrelated, in the same way that all the existing custom element reactions are unrelated to having a shadow DOM.

I'd be in favor of coupling it to being a custom element, as that's how the platform works today; certain element classes are always form controls and others are not, without the opportunity for individual elements to opt in or out. So I am curious to discuss more why @rniwa believes we should break the platform's existing model and let individual elements opt in to being form controls.

  1. Should we support non-element participants?
    I assume YES. Several people said it's necessary, and there were no objection.

Yes, we must support this use case.

  1. Should we support element-specific features such as participate-by-markup, &lt;label> association, interactive form validation.

There should be a way to make a custom element inside a form element to be able to participate in the form submission, and that's a hard requirement for us.

However, I don't think we need label element association, interactive form validation, etc... in MVP. That's a separate concern, which is more about creating a custom form control element, and not about sending custom data as a part of a form submission.

  1. If the consensus for the above issue is YES, we need something like Proposal B and need to define the details.

I do not think there is such a consensus.

That's a separate concern, which is more about creating a custom form control element, and not about sending custom data as a part of a form submission.

I highly suspect folks will couple these though (e.g., the SubmittableRichEdit class above), so anything we come up with will need to be able to address both, even if we only define/ship a subset at first. If folks will use it as such though I think that not addressing labeling straight away would be rather bad from an end user usability and accessibility perspective.

That's a separate concern, which is more about creating a custom form control element, and not about sending custom data as a part of a form submission.

I highly suspect folks will couple these though (e.g., the SubmittableRichEdit class above), so anything we come up with will need to be able to address both, even if we only define/ship a subset at first. If folks will use it as such though I think that not addressing labeling straight away would be rather bad from an end user usability and accessibility perspective.

Sure, I have no doubt they need to be able to work together but they don't need to be tied together in terms of API.

Lowest hanging fruit: add beforesubmit to form elements (also fires for submit() that exposes FormData, which includes existing form data).

(That's what we had agreement on to add.)

I just wanted to add some discombobulated thoughts as somebody who's been living the "submitting custom elements in forms" story for the last 2 years:

  • users really expect custom elements to work like input, and they do not understand or care why they are different. If a custom element can't be validated (but the native elements can), it will matter less, imo that it can be submitted to a form. If you have to override the form submission so that you can manually trigger custom element validation because the <form> doesn't do it, you could at that point just create your <input type=hidden> and submit everything anyway. I think validation is a key aspect of successfully submitting form data.
  • we've had to implement a label that works with custom elements, because having the native <label> not work with custom elements was a very bad story, and led to elements with dubious accessibility. It's a hard/annoying element to write correctly, because you have to forward both clicks and aria attributes manually
  • not all custom elements can have a shadow root, so I think requiring a shadow root to be eligible for form submission is bad. The example here is any custom element that needs to wrap/interact with an older API that was written before shadow DOM was a thing, and does global querySelectors (see: ACE editor, jQuery plugins, etc)

Here are two points I made during F2F:

  1. I'm very much uncomfortable executing any scripts during validation because we update the validation states when it's not safe to run scripts. So the callback approach wouldn't work. I think there was a rough consensus that we can use a push-model instead where each custom element would call setCustomValidity instead for this.
  2. Setting custom validation error message would open up a can of worms. For starters, there needs to be an API to specify where the error message should be shown relative to the element's bounding rect.

We simply couldn't come to consensus as to whether we allow non-element participants and whether it's okay to have a different API for element case and non-element case.

@notwaldorf FWIW, I think there still is interest in addressing the larger use case too. It's just that as far as implementer interest went only Google was comfortable with the proposal as-is. Others wanted more work on the proposal as per @rniwa's comment and also a better answer on how to expose .form, .setCustomValidity, et al.

I revised the document.

  • Switch Proposal A to an event-based approach.
  • Remove all synchronous callbacks
  • Now Proposal B is an extension of Proposal A

It doesn't address everything we discussed yesterday yet.

Issues with beforesubmit (or formdata as @tkent-google named it):

  • FormData is only for multipart/form-data, it's not intended for application/x-www-form-urlencoded (that's URLSearchParams). Not sure we should mix these. The problem with the latter is that it doesn't non-UTF-8 (we could not enable this feature in forms that do not use UTF-8 I suppose). And then there's text/plain too. Not sure how to deal with that... An alternative here might be that we only support multipart/form-data for now.
  • Currently in the specification we gather the "form data set" after firing invalid and submit events. We'll have to move these around, but that should not be observable. I'm guessing we want to fire this after invalid, but before submit? And we want to make this cancelable too?

(As for the issues raised in the linked document, I think it should be the same object, be mutable, and contain existing entries.)

FormData is only for multipart/form-data, it's not intended for application/x-www-form-urlencoded (that's URLSearchParams). Not sure we should mix these. The problem with the latter is that it doesn't non-UTF-8 (we could not enable this feature in forms that do not use UTF-8 I suppose). And then there's text/plain too. Not sure how to deal with that... An alternative here might be that we only support multipart/form-data for now.

My hope was that FormData would correspond well enough with the spec's concept of "form data set" that it would serve as an appropriate representation for exposing to JavaScript---even if ultimately the data gets transmitted via a non-multipart/form-data format. In particular, it seems like it's a high-fidelity representation of all the stuff you could put in a form.

Is that incorrect? Are there concrete mismatches where spec's form data set -> FormData -> application/x-www-form-urlencoded would lose data, compared to spec's form data set -> application/x-www-form-urlencoded?

I think we can use FormData for this purpose regardless of form encoding.
e.g.

  • dispatch the event with an empty FormData
  • Adding an empty type to each of name-value pairs in the FormData, then copy them into form data set

I'm not sure why @annevk thought FormData depended on multipart/form-data. Actually, WebKit and Blink use a FormData object to collect values from built-in form controls. They don't store type for entries in "construct the form data set" algorithm.

Currently in the specification we gather the "form data set" after firing invalid and submit events. We'll have to move these around, but that should not be observable. I'm guessing we want to fire this after invalid, but before submit? And we want to make this cancelable too?

IMO, new FormData(formElement) should include data provided by this API. So the event should be dispatched in "construct the form data set" algorithm.

I'm going to start an experimental implementation of the event-based approach in Google Chrome.

In F2F, we assumed the event name was beforeSubmit. IMO this should be changed to something else because this event should be dispatched in formElement.submit() case, which won't dispatch submit event, and new FormData(formElement) case too, which won't submit a form. Also, I received a feedback that an event name should be a verb and formdata isn't appropriate. Should it be constructFormData event?

If we dispatch the event in "construct the form data set" algorithm, event order would be:

Interactive form submission case:

  1. invalid if the form is invalid
  2. submit
  3. The event for this feature

Applications can cancel the form submission by preventDefault() for submit event.

formElement.submit() case:

  1. The event for this feature

new FormData(formElement) case:

  1. The event for this feature

It seems strange that we fire submit before constructing FormData. I guess what it is now submit event is really beforesubmit event since it's cancelable and fires before the actual submission. So it is indeed confusing to call this event beforesubmit.

Since this event is fired with a FormData, I don't think it makes sense to call it constructFormData. We also don't have any event with construct prefix. Note that the name of an event doesn't need to be a verb; we have event names such as slotchange and invalid that are not verbs.

The best I can think of right is formdataready but I'm sure there is a better name for this.

formdata seems okay to me. gatherformdata is a longer variant that came to mind. The name needs to be all lowercase, since we'll also add on* to the form element presumably.

ok, I'll apply formdata event type name.

I just started experimental implementation with the following behavior:

  • Event type name is formdata
  • Event interface is as the following:
[Exposed=Window]
interface FormDataEvent : Event {
  readonly attribute FormData formData;
};

Isn't it more useful at the end, so you can manipulate or add to data already collected? Otherwise we have to combine the data afterwards and this doesn't really end up being a low-level primitive (since combine isn't exposed).

Isn't it more useful at the end, so you can manipulate or add to data already collected?

I also think it's useful. However the current HTML specification doesn't use FormData to collect entries of built-in controls. If it's dispatched at the end, UA needs to convert form data set to FormData, dispatch the event, then convert back the FormData to form data set. It's complicated, and 'type' information is dropped.

It seems that should be doable, although it would require some reorganization perhaps (e.g., doing _charset_ more eagerly).

I would like to propose a async alternative to adress this issue also: w3c/html#1289 (comment)

When the form is valid,

  • Trigger this event feature.
  • Wait for event.waitUntil to be resolved so we have time to do some asynchronous validation.
    For example check if something already taken and maybe set field.setCustomValidity('username already taken'))
  • If form is still valid then submit the form

Adding asynchronous validation is an interesting idea, but should be considered as a separate feature proposal, and does not block this work.

If you'd like to propose that separate feature, I'd suggest filing it at https://github.com/whatwg/html, since that is the repository for the HTML specification browsers actually implement.

I just started experimental implementation with the following behavior:

The latest Google Chrome Canary has the experimental implementation. The feature is enabled by chrome://flags/#enable-experimental-web-platform-features .

I'll try to modify the HTML specification so that we can move the event dispatch to the end of the algorithm.

Also, I'll restart the discussion about custom-element-based API.

Also, I'll restart the discussion about custom-element-based API.

Chrome's Autofill team told me that they need a way to distinguish form-associated custom elements from general custom elements, and a way to read/write their values. So formdata event isn't enough. Google strongly wants something like Proposal B.

I think we need to do the following in addition to the current Proposal B.

  • Form-associated custom elements must provide value setter.
  • Provide a way to tell UA the current value of a form-associated custom element

For example,

class MyControl extends HTMLElement {
  ...
  set value(v) {
    ...
    window.customElements.setFormControlValue(this, v);
  }
  • UA can get such values without invoking user's JavaScript code because setFormControlValue stored values in UA beforehand.
  • MyControl doesn't need to handle formdata event
  • Autofill feature calls the value setter. It executes arbitrary JavaScript code, but UA can avoid its complexity by calling the value setter asynchronously.

As for the idea of exposing base class of form controls, which we discussed in the last F2F, I think it's a good idea in general. However I'm not sure if it's possible to upgrade native interface of undefined custom elements.

var control = document.createElement('my-control');
// 'control' should be an instance of HTMLElement
customElement.define('my-control', class MyControl extends HTMLFormControlElement { ... });
// 'control' should be upgraded to HTMLFormControlElement, and MyControl.

I feel like I suggested the subclassing idea myself but HTMLFormControlElement is not a real interface in HTML today, and I'm afraid introducing one would cause a compatibility nightmare.

It appears to me that we do need to come up with some mechanism to expose browser engine states and APIs that are only accessible to custom elements implementations. e.g. setFormControlValue in the above example shouldn't be something users of custom elements can simply call and override the value.

In the past, we've used ShadowRoot for that purpose. Does it make sense to do that here?

It appears to me that we do need to come up with some mechanism to expose browser engine states and APIs that are only accessible to custom elements implementations.

Yes. We want such APIs. However, we should not make ShadowRoot mandatory for custom elements. We should introduce a dedicated interface instead. For example,

dictionary ValidityStateFlags {
  boolean valueMissing = false;
  boolean typeMismatch = false;
  boolean patternMismatch = false;
  boolean tooLong = false;
  boolean tooShort = false;
  boolean rangeUnderflow = false;
  boolean rangeOverflow = false;
  boolean stepMismatch = false;
  boolean badInput = false;
};

interface HTMLElementPrimitives {
  void setFormControlValue(DOMString value);
  void setValidityState(ValidityStateFlags flags);
  ...
};
class MyControl extends HTMLElement {
  // New lifecycle callback?  'connectedCallback' is too late. Form control value is
  // meaningful even if an element is disconnected.
  // UA creates an HTMLElementPrimitives object for this element, and passes it here.
  void createdCallback(primitives) {
    this._primitives = primitives;
    primitives.setFormControlValue(this.getAttribute('value'));
  }

  set value(v) {
    ...
    this._primitives.setFormControlValue(v);
  }

(I keep forgetting to add this for posterity, but I made a demo of the implementation currently in Chrome: demo, code)

Chrome's Autofill team told me that they need a way to distinguish form-associated custom elements from general custom elements, and a way to read/write their values. So formdata event isn't enough. Google strongly wants something like Proposal B.

No one raised concern about the above paragraph for four weeks. For now I assume it's ok to extend Custom Elements API for form controls in addition to the formdata event.

#187 (comment)

It seems that should be doable, although it would require some reorganization perhaps (e.g., doing charset more eagerly).

We have done this reorganization of specifications. I think now it's easy to dispatch formdata event at the end of the loop, and I'll start to add formdata event to the HTML specification if no one objects.

It's okay to add custom elements API for form associated elements as long as there is a way to add more data without creating any custom elements.

Okay, it seems the event would satisfy that requirement.

as long as there is a way to add more data without creating any custom elements.

Okay, it seems the event would satisfy that requirement.

Yes, I suppose we standardize both of the formdata event and the custom element extension, and the former doesn't depend on custom elements.

I updated the document. Now it contains the idea of HTMLElementPrimitives. Also, the current proposal is capable to handle multiple entries for a single custom element, like <input type=image> and dirname content attribute.

HTMLElementPrimitives is very similar to ElementStates in #738 (comment). This issue and #738 should have the identical way to provide helper APIs.

I'm starting a prototype implementation of the current Proposal B on my local machine.

I asked a TAG review for my proposal. w3ctag/design-reviews#305

FYI.
I implemented almost all features of the proposal in Chrome.
Since Google Chrome 73.0.3632.0 canary, you can try the proposal by enabling chrome://flags/#enable-experimental-web-platform-features.

One missing feature is form-validation UI.

My findings from implementatoin experience:

  • UA should support :invalid and :valid pseudo classes for form-associated custom elements
    Because ElementInternals has setValidity() operation, UA knows validity state of a form-associated custom element precisely. UA can support :invalid and :valid without additional API for pseudo class matching

  • UA should support :enabled and :disabled pseudo classes for form-associated custom elements
    The proposal has "disabledStateChangedCallback" because of complexity of <fieldset disabled> in ancestors. UA needs to track disabled state of form-associated custom elements precisely. So, UA can support :enabled and :disabled without additional API for pseudo class matching.

  • Form-associated custom elements should not support form-association by HTML parser. (step 12 of create an element for a token)
    If a parsed element looks to be a custom element but it is not defined yet or it will be upgraded later, HTML parser can't know whether it's a form-associated custom element or not. Storing 'form element pointer' in each of undefined elements isn't reasonable. IMO form-associated custom elements should not support form-associated by HTML parser for consistency.

@tkent-google I'm not sure what's the best place to provide some feedback and questions about this, so I will add it here in the original issue.

We ran this by some folks at salesforce, here is the feedback so far:

  • we are very excited about the custom element association with forms, not so much about the event-based form participation, which we don't plan to use.
  • form-validation is a big one for us, we will wait until it is implemented behind a flag to provide concrete feedback, but at first glance, it seems like it will be enough.
  • we still have questions about how to mimic the behavior of (multiple form elements competing for one name with the corresponding coordination of their respective states)
  • form reset protocol is not very well detailed in the doc. by calling form.reset() or by clicking on the reset button, how a custom element can react to that?

@caridy

we will wait until it is implemented behind a flag

The latest Google Chrome Dev and Canary have this feature including form validation.

(multiple form elements competing for one name with the corresponding coordination of their respective states)

I don't catch this part. Would you elaborate on this, or show some examples please?

form reset protocol

I think existing 'reset' event is enough. A form-associated custom element listens to the event on the owner form element, and posts a task in the event handler, then the task checks defaultPrevented and resets the element's state if it's false.

@tkent-google

Here is an actual example:

<template>
  <form>
    <p>Select a maintenance drone:</p>
 
    <div>
      <fancy-radio name="drone" value="huey">
      <label for="huey">Huey</label>
    </div>

    <div>
      <fancy-radio name="drone" value="dewey">
      <label for="dewey">Dewey</label>
    </div>

    <div>
       <fancy-radio name="drone" value="louie">
       <label for="louie">Louie</label>
    </div>
  </form>
</template>

There are two ways to implement that fancy-radio, a) extending input, b) wraping the input in a custom element that participates in the form. Option a) is really not an option ATM due to the limitations of the is attribute, so let's focus on b.

Making the 3 instance to participate in the form works great, each of them will override the drone value when needed by calling setFormValue, but the problem is really about the coordination between them to uncheck the others when a new one is checked without requiring the consumer to do this coordination manually.

As for the reset mechanism, yes, certainly listening for it at the form level will do the trick, but that has one issue for us: leaking the form reference is undesirable, in our case, at salesforce, we have extra restrictions on what you can see/observe. In our case, we will probably restrict formAssociatedCallback to prevent leaking that form. A callback (e.g.: formResettedCallback) works better for us.

In other words, we favor a solution that does not require interacting with the form element directly.

Thanks for the feedback Caridy! On your example, this is the same work you'd have to do with a normal <input type="radio">, right? Adding a new automated coordination mechanism seems out of scope for this proposal, since the goal is to give the same capabilities to custom elements that built-in elements have, and no such automated coordination mechanism exists for built-ins. Or am I misunderstanding?

Similarly, for reset, it seems like the platform as it exists provides the needed primitives, and you'd be able to provide additional helpers to work around your platform's additional restrictions. Right?

@domenic no, that's precisely my point. My example above is just a copy/paste of https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio, but using a fancy-radio instead of input. When selecting a different radio, the previously selected one switched from checked to unchecked. That's the coordination that I was referencing to. The consumer of the input has to do nothing, while the consumer of fancy-radio will have to keep track of the state, unchecked the previously selected, and so on. Unless I'm missing something obvious about this proposal.

@domenic @tkent-google the radio coordination is definitely a very edge case that can be solved using some other mechanism (some internal machinery in the fancy-radio does the trick), it is not pretty, but it is doable, on top of radios are not a thing these days. So, don't worry much about it.

The reset though is a little bit more cumbersome for us, in case you wonder.

Ah, yeah, I see what you mean. I do think that kind of coordination is control-specific. In particular I think we already have all of the primitives needed to implement an analog of https://html.spec.whatwg.org/multipage/input.html#radio-button-group 's "When any of the following phenomena occur" in a custom element.

@tkent-google I'm still struggling to get the example to work on canary (Version 73.0.3652.0) after activating the flag for #enable-experimental-web-platform-features. Specifically, the call to this.attachInternals still complains saying that it is not a function, trimmed down version of the code:

        class Foo extends HTMLElement {
            static get formAssociated() { return true; }
            constructor() {
                super();
                this.internals_ = this.attachInternals();
            }
            // must be a public API
            get form() {
                return this.internals_.form;
            }
            checkValidity() {
                return this.internals_.checkValidity();
            }
            set value(v) {
                this._value = v;
                this.internals_.setFormValue(v)
            }
            get value() {
                return this._value;
            }   
        }
        customElements.define('x-foo', Foo);

any hint? at some point in #758 we talked about using the 3rd argument on the define() call, but I don't see any mention in your document, I assume that is not the case today.

@caridy, thank you for the explanation. As @domenic already wrote, coordination like built-in radio buttons can be implemented without UA's help. I made an example of radio-button-like custom element; https://jsfiddle.net/int32_t/dep4Lyhj/4/

I found following issues during developing the example:

  • setFormValue('', new FormData()) should submit nothing. The current Chrome implementation adds name= to the query string. It was unclear in the proposal document.
  • I assumed UA called the mandatory value setter to restore page state. However control's value and control's state are separated in built-in input[type=radio] and input[type=checkbox], and their value setter doesn't change their checkedness state. We should rename the mandatory setter to something like restoreState()

As for the reset callback, I think it's ok to add it. I thought form reset is not used nowadays, and no one wanted it.

I'm still struggling to get the example to work on canary (Version 73.0.3652.0) ...

It should work. Does https://jsfiddle.net/int32_t/dep4Lyhj/4/ work for you?

... the 3rd argument on the define() call, ...

I changed it from define() flag to static field of the class because the latter works better for class inheritance.

@tkent-google great help, thanks. more feedback coming soon.

@tkent-google There is no clearFormValue analogous to setFormValue in element internals. This means once we have set this form value, something will be sent to the server. This prevents implementation of a checkbox like control(I am trying to implement a toggle switch) where when the checkbox is checked the data is sent, but if it is unchecked, nothing is sent to the server.

Re: reset callback
I wonder whether we should use custom element reaction queue for the reset callback. Pressing input[type=reset] doesn't go through any IDL attributes and operations, so there is no ways to add [CEReactions]. If we use custom element reaction queue for it, we need to copy CEReactions steps into reset algorithm in order to invoke queued reset callbacks?

@atishay Since Google Chrome 73.0.3665.0 canary, setFormValue('foo', new FormData()) won't submit anything for the element. https://jsfiddle.net/int32_t/dep4Lyhj/4/ demonstrates it.

@tkent-google I might be misremembering, but I was pretty sure the backup element queue was specifically used for [CEReactions] in such situations. By my reading, we would do https://html.spec.whatwg.org/#enqueue-an-element-on-the-appropriate-element-queue , and step 1 would be true, then we would immediately queue a microtask to run the reaction. Which seems like the desired setup. Right?

@domenic That's right! Thanks.
So just enqueuing a custom element callback reaction with "formResetCallback" in reset should work well. I'll update the proposal document for formResetCallback and the Chrome implementation.

@tkent-google this.internals_.setFormValue(this.getAttribute('name'), new FormData()); works but this.internals_.setFormValue(new FormData()); does not. It creates and empty form data.

On a side note, I find setFormValue('foo', new FormData()) inconsistent. We have removeItem in localStorage which does not require developers to understand this fine detail. Setting undefined is not clearing it. Even FormData has delete. setFormValue is used with a string in most cases. Having an empty FormData is confusing.

One missing feature is form-validation UI.

@tkent-google is there another spec that is going to handle form/field validation? Or is the plan to handle it in the form-participation spec?


Playing around a bit with this jsfiddle: https://jsfiddle.net/int32_t/dep4Lyhj/4/

When using <input type=file name=test />, providing a file, it doesn't seem to be passing the file through? (Maybe I'm misunderstanding?)

When using optional native inputs (<input name=test />) their name seems to be set on the formData even if the field wasn't touched/modified

Not sure it is associated with this feature but enabling the chrome flag (chrome://flags/#enable-experimental-web-platform-features) is causing my lit-elements to break. https://stackblitz.com/edit/65s7tj-olpifa?file=index.html

@caridy

radios are not a thing these days

I didn't get that memo. Radios are alive and kicking around here! 😛
But I agree on the internal mechanism. That's easily achievable with a static property.

@atishay
ok, I changed setFormValue() so that it accepts setFormValue(null), which means no data submission. Chrome's implementation doesn't follow it yet.

@stramel
"One missing feature is form-validation UI" was an implementation status in Chrome Canary. The proposed API has capability to support form-validation UI. You can see a validation bubble by clicking 'Submit' button in https://jsfiddle.net/dep4Lyhj/6/ with Chrome Dev/Canary with the experimental feature flag.
I don't understand the question about <input type=file name=test/>

The current status of specification/test changes:

Event-based participation

ElementInternals for form-associated custom elements

Form-associated custom elements

The proposal document is here. It would be easier to read the proposal than specification PRs.

@rniwa Would you review 'form-associated custom elements' part of the proposal or its specification PR in an implementor point of view please? I'd like to merge the PR if the API has no problems.

@annevk Would you ask someone who is responsible for Custom Elements implementation and/or HTML Forms implementation in Mozilla to review 'form-associated custom elements'?

@tkent-google Thank you for clarifying the scope of the spec and changes. I found it very useful!

I will clarify my previous comments:

One missing feature is form-validation UI.

Your comment clarified part of this for me. It does handle validation at the form/element level. As well, it offers :valid/:invalid selectors for styling.

The only complaint that I still have here is that the UA's validation UI is forced if you want to validate before submit. Using the novalidate attribute will disable the UA's validation UI but allows a submission to happen with an invalid form.

When using , providing a file, it doesn't seem to be passing the file through? (Maybe I'm misunderstanding?)

Sorry, this was more of a question. I was missing the enctype="multipart/form-data" on this.

When using optional native inputs () their name seems to be set on the formData even if the field wasn't touched/modified

This was also a question about the expected request when using optional fields. Should the optional field appear in the request? Currently, when using <input name="test" /> the resulting request has "test": "" in the form property. I was expecting the optional fields not to be present unless they had values or were modified.

Not sure it is associated with this feature but enabling the chrome flag (chrome://flags/#enable-experimental-web-platform-features) is causing my lit-elements to break.

This appears to have been fixed. Thanks!


Overall, this looks pretty good from what I have tested with my one comment about the UA's validation UI. It would be nice to be able to independently disable the UA's validation UI (without disabling validate on submit) or provide a way to customize the UA's validation UI instead.

Overall, this looks pretty good from what I have tested with my one comment about the UA's validation UI. It would be nice to be able to independently disable the UA's validation UI (without disabling validate on submit) or provide a way to customize the UA's validation UI instead.

This isn't specific to custom elements though, right? It sounds like a separate, orthogonal feature request for all form-associated elements, whether they're custom or not.

This isn't specific to custom elements though, right? It sounds like a separate, orthogonal feature request for all form-associated elements, whether they're custom or not.

@domenic That's correct. This is just a general issue I have with the existing form validation. Nearly every employer that I have worked for doesn't like/want to see the UA's validation UI. Unfortunately, the only way to disable it is through novalidate.

Note: This comment is off topic.

disable the UA's validation UI (without disabling validate on submit)

It's possible by a small amount of code.

  • Add novalidate to a <form>.
  • Register submit event handler.
  • The handler can call checkValidity() of the <form> and/or its associated controls, and can show your own UI for validation errors.
  • The handler should call event.preventDefault() to interrupt submission if the form is invalid.

@rniwa Would you review 'form-associated custom elements' part of the proposal or its specification PR in an implementor point of view please? I'd like to merge the PR if the API has no problems.

@annevk Would you ask someone who is responsible for Custom Elements implementation and/or HTML Forms implementation in Mozilla to review 'form-associated custom elements'?

I still want implementer feedback on form-associated custom elements.
We're going to have F2F in this month. However, I won't attend it, and raising concrete issues before the F2F would be helpful.

To be clear, I have asked, but folks are busy. I've done a partial review as you saw though and will hopefully be able to take another look before the F2F.

Minutes of Spring 2019 F2F

Re: Form-associated non-custom elements, ElementInternals for built-in elements

It is an issue separated from form-associated custom element. So we can discuss it after finishing this issue.
We need to take care of many edge cases. For example, if <a> was form-associated, name attribute would have two meanings.
I'm afraid it will make the specification and implementations much complicated.

Re: Form validation for compound controls

Suppose that we have a form-associated custom element with three internal fields.

<card-input required>
  #shadow-root
    <input id=cardno pattern="[0-9]{15,16}" required>
    <input id=expiration type=month required>
    <input id=cvc pattern="[0-9]+" required>
</card-input>

The owner <form> of <card-input> doesn't take care of these internal fields at all. So the implementation of <card-input> should call ElementInternals.setValidity() if one of them is invalid.

Validation UI would have a trouble. The current proposal and the Chrome implementation just show a validation UI on <card-input>. However, it should be shown on an invalid internal field.
A possible solution would be to add an optional Element argument to ElementInternals.setValidity(). this.#internals.setValidity({valueMissing: true}, 'Please fill out this field with the expiration month of the card', this.#expiration); will show a validation popup on expiration field.

What do you think?

Apologies if this has been discussed. I couldn't find them. In testing form-associated custom elements I ran into the following two cases:

  • Looks like implicit submission does not get triggered on form-associated Custom Elements in Chrome's implementation. Should they be? Or is there already a way to hook that up?

  • It doesn't seem like autofocus is supported either. Should they be, like other form controls? Reference.

I'll defer to @tkent-google as the real expert, but some quick notes while he's away for the weekend:

  • Implicit submission is an interesting question. We discussed this briefly in whatwg/html#4187 (comment) . You could probably make this work manually with a keydown listener for the Enter key. It sounds like you think it's worth making this work more generally? I was under the impression web developers did not like this feature but that's based only on vague memories.

  • Autofocus spec seems weird. The attribute is only defined for form controls, but the processing model never checks if something is a form control. My guess is that since the spec is confusing/incomplete, it was easy to miss this while updating the spec and implementation. But @tkent-google can confirm.

You could probably make this work manually with a keydown listener for the Enter key.

Making this work manually and matching the implicit submission behavior would require us to query for the default button as submitter, and submit its value too (via whatwg/html#4187 I guess).

It sounds like you think it's worth making this work more generally?

Given the above, yes, it'd be more ideal if this is handled automatically with the same algorithm.

I was under the impression web developers did not like this feature but that's based only on vague memories.

In my experience GitHub users rely on this behavior a lot, and always promptly report the bug when we break it. 😬

cc @sfdciuie who raised the issue about compound controls during the F2F meeting last week. I think @tkent-google explanation makes sense, we should be fine with something like that.

Implicit submission

With whatwg/html#4187, manual implicit submission code would be:

let form = this.#internals.form;
// form.querySelector(':default') doesn't work due to 'form' content attribute.
for (let control of form.elements) {
  if (control.matches(':default')) {
    if (!control.matches(':disabled'))
      form.requestSubmit(control);
    return;
  }
}
form.requestSubmit();

Introducing a flag like this.#internals.submitImplicitly = true will eliminate the above code. IMO, it's a nice-to-have feature, and not a mandatory feature for the first version of form-associated custom element.

:default

We should be able to make a submit button with form-associated custom element. I just found a user agent needs to check if a form-associated custom element is a submit button or not to handle the :default pseudo class correctly. We need a flag like this.#internals.submitButton = true.

Autofocus

I proposed to move autofocus attribute to HTMLElement. whatwg/html#4563

First let met say that all of these things seem like they can be done on top of the current FACE (form-associated custom elements) pull request. That's a good sign :).

So I think there are two issues with implicit submission:

  1. Letting implicit submission work on a FACE, so that when you press Enter while focusing that FACE, it submits the form.
  2. Letting implicit submission click a FACE submit button, so that when you press Enter while focusing a normal input type=text, it clicks your FACE submit button.

With whatwg/html#4187, manual implicit submission code would be:

This is about (1), from what I can tell.

I think form.requestSubmit(control) should probably be control.click() to perfectly imitate the spec. ("must cause the user agent to fire a click event at that default button.")

But I also wonder, it seems like you'd need to add code like that to an keydown (keypress? keyup?) handler, and filter for the Enter key, right? That seems a bit fragile since it's assuming the Enter key is correct, but that's just prevailing convention... Maybe the missing piece is some kind of "requestimplicitsubmit" event, which gets fired on the Enter key or similar? Or a custom element callback??

I also think there's room for making this easier by adding a HTMLFormElement.prototype.defaultButton which would let you skip the loop.

In general I like the idea of people adding this manually, but doing it using platform-provided primitives that make it intuitive. So with all of my above it might be something like

this.addEventListener("requestimplicitsubmit", () => {
  const button = this.#internals.form.defaultButton;
  if (!button.disabled) {
    button.click();
  }
});

To reiterate, I am happy with these pieces being done after the initial version of FACE.

:default

This seems to start discussing (2). In particular I think this.#internals.submitButton = true should cause (2) to start working.

I also wonder about how this relates to #738 (comment) about allowing you to manually toggle states like :checked. (Did we open a dedicated issue for that, separate from :state()?) In particular, should we allow you to manually toggle :default? That seems necessary to get some of its other behaviors, like for <option selected> and <input type=checkbox checked>. But manual toggling seems in conflict with deriving it from internals.submitButton.

I've honestly never seen anyone use :default before your code example today so I am not sure how high priority this is. And especially unsure how high priority it is to support it for the non-button cases. But it's worth thinking about.

First let met say that all of these things seem like they can be done on top of the current FACE (form-associated custom elements) pull request. That's a good sign :).

I also think so. We don't need incompatible changes against whatwg/html#4383 in order to resolve known issues.

So, we, Chromium project, would like to start shipping process of both of event-based participation and form-associated custom element early next week. Does anyone still want to review these APIs?