New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need callback for form submit data #187

Open
hayatoito opened this Issue Jul 6, 2015 · 63 comments

Comments

Projects
None yet
@hayatoito
Member

hayatoito commented Jul 6, 2015

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

@StokeMasterJack

This comment has been minimized.

StokeMasterJack commented Jun 29, 2016

Here is my proposal for addressing this issue:

http://dave-ford.blogspot.com/2016/06/web-components-and-form-submit-fields.html

@domenic

This comment has been minimized.

Contributor

domenic commented Sep 19, 2016

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".)
@rniwa

This comment has been minimized.

Contributor

rniwa commented Sep 20, 2016

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.

@lukasoppermann

This comment has been minimized.

lukasoppermann commented Oct 13, 2016

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

@domenic

This comment has been minimized.

Contributor

domenic commented Oct 13, 2016

Yes

@lukasoppermann

This comment has been minimized.

lukasoppermann commented Oct 13, 2016

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.

@domenic

This comment has been minimized.

Contributor

domenic commented Oct 13, 2016

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".

@lukasoppermann

This comment has been minimized.

lukasoppermann commented Oct 13, 2016

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

@domenic

This comment has been minimized.

Contributor

domenic commented Oct 13, 2016

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.

@PleasantD

This comment has been minimized.

PleasantD commented May 2, 2017

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?

@treshugart

This comment has been minimized.

treshugart commented May 2, 2017

@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.

@rniwa

This comment has been minimized.

Contributor

rniwa commented May 9, 2017

We've made a concrete proposal three years ago in https://lists.w3.org/Archives/Public/public-webapps/2014JanMar/0448.html

@annevk annevk changed the title from [Custom]: Need callback for form submit data (bugzilla: 24603) to Need callback for form submit data Sep 6, 2017

@robdodson robdodson referenced this issue Sep 27, 2017

Closed

<form> #120

@robdodson robdodson referenced this issue Oct 10, 2017

Merged

Ratcheting #124

@tkent-google

This comment has been minimized.

tkent-google commented Mar 6, 2018

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.

@annevk

This comment has been minimized.

Member

annevk commented Mar 7, 2018

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.)

@domenic

This comment has been minimized.

Contributor

domenic commented Mar 8, 2018

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?

@tkent-google

This comment has been minimized.

tkent-google commented Mar 13, 2018

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.

@tkent-google

This comment has been minimized.

tkent-google commented Mar 29, 2018

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
@rniwa

This comment has been minimized.

Contributor

rniwa commented Mar 29, 2018

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.

@annevk

This comment has been minimized.

Member

annevk commented Mar 29, 2018

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.

@tkent-google

This comment has been minimized.

tkent-google commented Apr 4, 2018

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;
};
@annevk

This comment has been minimized.

Member

annevk commented Apr 4, 2018

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).

@tkent-google

This comment has been minimized.

tkent-google commented Apr 4, 2018

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.

@annevk

This comment has been minimized.

Member

annevk commented Apr 4, 2018

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

@jimmywarting

This comment has been minimized.

jimmywarting commented Apr 11, 2018

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
@domenic

This comment has been minimized.

Contributor

domenic commented Apr 11, 2018

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.

@tkent-google

This comment has been minimized.

tkent-google commented Apr 12, 2018

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.

@tkent-google

This comment has been minimized.

tkent-google commented May 8, 2018

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.
@rniwa

This comment has been minimized.

Contributor

rniwa commented May 8, 2018

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?

@tkent-google

This comment has been minimized.

tkent-google commented May 14, 2018

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);
  }

@notwaldorf

This comment has been minimized.

notwaldorf commented May 29, 2018

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

@tkent-google

This comment has been minimized.

tkent-google commented Jun 5, 2018

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.

@tkent-google

This comment has been minimized.

tkent-google commented Jun 5, 2018

#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.

@rniwa

This comment has been minimized.

Contributor

rniwa commented Jun 6, 2018

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.

@annevk

This comment has been minimized.

Member

annevk commented Jun 6, 2018

Okay, it seems the event would satisfy that requirement.

@tkent-google

This comment has been minimized.

tkent-google commented Jun 12, 2018

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.

@tkent-google

This comment has been minimized.

tkent-google commented Jun 18, 2018

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

@tkent-google

This comment has been minimized.

tkent-google commented Sep 20, 2018

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment