Skip to content
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

Dispatch VALUE_CHANGE on amp-bind value changes #22840

Merged
merged 8 commits into from Jun 18, 2019

Conversation

xrav3nz
Copy link
Contributor

@xrav3nz xrav3nz commented Jun 14, 2019

This introduces a new AmpEvents.VALUE_CHANGE event. It is dispatched
when amp-bind mutates form input values, specifically:
<input [value]>, <textarea [text]>, <input [checked]>, and
<option [selected]>.

A custom event is chosen over a mutation observer, because binding
[text] on textarea does not change the DOM (and it's expected), thus
a mutation observer won't pick up the change. The same applies to
input elements, when we eventually have two separate bindings for
[value] and [defaultValue].

Part of #22534.

/cc @GoTcWang @choumx thanks!

Copy link
Contributor

@GoTcWang GoTcWang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General ideas LG.
Commented on future use cases and naming, feel free to plus Will for answers.

extensions/amp-bind/0.1/test/integration/test-bind-impl.js Outdated Show resolved Hide resolved
src/amp-events.js Outdated Show resolved Hide resolved
@xrav3nz xrav3nz marked this pull request as ready for review June 14, 2019 20:30
@xrav3nz

This comment has been minimized.

extensions/amp-bind/0.1/bind-impl.js Outdated Show resolved Hide resolved
*/
function doesPropertyAffectFormValue(tagName, property) {
switch (property) {
case 'disabled':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider [hidden] attribute as well in this case then? Feel free to left a TODO and resolve in another PR.

Note that hidden attribute can be mutated on the ancestry of a form field.

Copy link
Contributor Author

@xrav3nz xrav3nz Jun 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh right. So there are:

  • disabled for form fields and fieldsets
  • hidden for essentially all elements
  • type for <input>

I am still leaning towards using a mutation observer on the form for all of the above, and leave AmpEvents.FORM_VALUE_CHANGE as close to the native InputEvent as possible. (It's also semantically cleaner as Will mentioned)

Thoughts? @choumx @GoTcWang

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup that also works.
I'll left it to Will to make the call.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

extensions/amp-bind/0.1/test/integration/test-bind-impl.js Outdated Show resolved Hide resolved
This introduces a new `AmpEvents.VALUE_CHANGE` event. It is dispatched
when `amp-bind` mutates form input values, specifically:
`<input [value]>`, `<textarea [text]>`, `<input [checked]>`, and
`<option [selected]>`.

A custom event is chosen over a mutation observer, because binding
`[text]` on `textarea` does not change the DOM (and it's expected), thus
a mutation observer won't pick up the change. The same applies to
`input` elements, when we eventually have two separate bindings for
`[value]` and `[defaultValue]`.
@xrav3nz xrav3nz force-pushed the amp-bind/value-change-event branch from d764b59 to d356e4d Compare June 17, 2019 17:45

if (isPropertyAFormValue(element.tagName, property)) {
const dispatchAt =
element.tagName === 'OPTION' ? element.closest('SELECT') : element;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why is this necessary? Also use closestAncestorElementBySelector() helper in dom.js in case Element.closest is not available.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! The native InputEvent is also dispatched at the parent <select> when the selected <option> changes. I think it makes sense, since it is the value of the <select> element that is changed.

I just added a comment explaining this!

if (dispatchAt) {
dispatchAmpFormValueChangeEvent(this.localWin_, dispatchAt);
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Can we keep everything in a single class function? It would also save you from checking tagName twice.

this.dispatchFormValueChangeEventIfNecessary_(element, property);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To only check tagName once, the switch gets a bit intense with the nested ifs and early returns: https://gist.github.com/xrav3nz/6fd7b048080aedbaa1e87c418b4193e4 Are you okay with this?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, probably better as is. Speaking of switch statements, a lookup map might be more readable.

Optional:

/** @const {!Object<string, !Array<string>>} */
const FORM_VALUE_PROPERTIES = {
  'INPUT': ['checked', 'value'],
  'OPTION': ['selected'],
  'TEXTAREA': ['text'],
};

dispatchFormValueChangeEventIfNecessary_(element, property) {
  const props = FORM_VALUE_PROPERTIES[element.tagName];
  if (props && props.includes(property)) {
    ...
  }
}

Copy link
Contributor Author

@xrav3nz xrav3nz Jun 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm gonna to leave it as is Updated! Thanks again for the reviews! 💪

Copy link
Contributor

@GoTcWang GoTcWang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@dreamofabear dreamofabear merged commit 0be1ae1 into ampproject:master Jun 18, 2019
@dreamofabear
Copy link

Nice work, thanks for contributing.

@xrav3nz xrav3nz deleted the amp-bind/value-change-event branch June 18, 2019 19:11
thekorn pushed a commit to edelight/amphtml that referenced this pull request Sep 11, 2019
* Dispatch VALUE_CHANGE on `amp-bind` value changes

This introduces a new `AmpEvents.VALUE_CHANGE` event. It is dispatched
when `amp-bind` mutates form input values, specifically:
`<input [value]>`, `<textarea [text]>`, `<input [checked]>`, and
`<option [selected]>`.

A custom event is chosen over a mutation observer, because binding
`[text]` on `textarea` does not change the DOM (and it's expected), thus
a mutation observer won't pick up the change. The same applies to
`input` elements, when we eventually have two separate bindings for
`[value]` and `[defaultValue]`.

* more elaborate test names

* rename event to FORM_VALUE_CHANGE

* dispatch event at <select> when <option> changes

* extract FORM_VALUE_CHANGE_EVENT_ARGUMENTS in test

* dont use `closest` & move everything into a helper

* refactor to use a lookup map instead

* avoid a linear `includes` search
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants