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
Exposing input change event and form.submit action #5707
Conversation
@@ -138,6 +135,14 @@ export class AmpForm { | |||
/** @const @private {!./form-validators.FormValidator} */ | |||
this.validator_ = getFormValidator(this.form_); | |||
|
|||
this.actions_.installActionHandler(this.form_, invocation => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dvoytenko is this the right thing to do here? Or using addGlobalMethodHandler
a better option?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like installActionHandler
is definitely better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack
// TODO: ASK @dima about this? Is this limited to AMP-elements for security | ||
// reasons? If yes, shouldn't one check the tagname rather than the id? | ||
//user().assert(target.id && target.id.substring(0, 4) == 'amp-', | ||
// 'AMP element is expected: %s', debugid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dvoytenko why is this limited to AMP elements? Would it be ok to relax this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Probably would be ok to relax. The intention here is really to reduce a probability of error by installing this handler on an accidental element. But I just checked and it looks like installActionHandler
is already a restricted API in presubmits. So we don't need to worry too much about misuse.
A slightly bigger problem is on the trigger side - where you also disabled AMP
check. The issue is that if a publisher accidentally specifies the wrong target, they'll never know - a very easy mistake to make. So, I think, we still need some form of whitelist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
// TODO: ASK @dima about this? Is this limited to AMP-elements for security | ||
// reasons? If yes, shouldn't one check the tagname rather than the id? | ||
//user().assert(target.id && target.id.substring(0, 4) == 'amp-', | ||
// 'AMP element is expected: %s', debugid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Probably would be ok to relax. The intention here is really to reduce a probability of error by installing this handler on an accidental element. But I just checked and it looks like installActionHandler
is already a restricted API in presubmits. So we don't need to worry too much about misuse.
A slightly bigger problem is on the trigger side - where you also disabled AMP
check. The issue is that if a publisher accidentally specifies the wrong target, they'll never know - a very easy mistake to make. So, I think, we still need some form of whitelist.
@@ -138,6 +135,14 @@ export class AmpForm { | |||
/** @const @private {!./form-validators.FormValidator} */ | |||
this.validator_ = getFormValidator(this.form_); | |||
|
|||
this.actions_.installActionHandler(this.form_, invocation => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like installActionHandler
is definitely better.
@@ -163,20 +173,24 @@ export class AmpForm { | |||
* invalid. stopImmediatePropagation allows us to make sure we don't trigger it | |||
* | |||
* | |||
* @param {!Event} e | |||
* @param {?Event} optEvent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
opt_event
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@dvoytenko added the whitelist, PTAL 👀 - will be adding tests in the next commit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great. Couple of requests and looking forward to tests.
@@ -152,6 +157,11 @@ export class AmpForm { | |||
onInputInteraction_(e); | |||
this.validator_.onInput(e); | |||
}); | |||
this.form_.addEventListener('change', e => { | |||
const input = e.target; | |||
// TODO(#5702): Consider a debounced-input event for text-type inputs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO(ldap, #issue)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -271,9 +273,17 @@ export class ActionService { | |||
return; | |||
} | |||
|
|||
// Check if this is an element has a known action. | |||
const supportedActions = ELEMENTS_ACTIONS_MAP_[lowerTagName]; | |||
if (target[ACTION_QUEUE_] && supportedActions && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you not initializing ACTION_QUEUE_
right here? Are risk conditions do not apply here? Is it even worth the cost?
Btw, this if
and the previous one (target.id ~= amp-
) can be combined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with initializing ACTION_QUEUE_
here is basically initialize it to what? An empty array? This case specifically will probably be reached if the element uses installActionHandler
method to initialize the ACTION_QUEUE_
to be an object with a fake push
method to allow executing the handler passed.
So wouldn't initializing it here basically fail silently for these cases? We could throw a better error when not initialized - basically user().error('Action queue not initialized, probably tried to execute before calling installActionHandler on %s', this.element)
- or something like that. No?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite. We indeed initialize to an empty array. You can see it just 5 lines above for an amp-
case (I mentioned that this should probably be the same code). What this gives you is that it buffers up actions in an array until installActionHandler
is called. As you can see here, when installActionHandler
is called, it goes over buffered actions and sends them to the handler. This way we remove risk condition between when a user can activate an action and when installActionHandler
is called. That makes sense since installActionHandler
installation depends on asynchronous script download.
In cases like this it's possible that it's "too late" to execute an action. If this happens, we can add "deferred" flag to an action that's not executed immediately. We do this here, but to my knowledge no actions use it yet. So we can punt on this here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sense! Thanks for explaining. I'll update the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -163,9 +167,6 @@ export class ActionService { | |||
*/ | |||
installActionHandler(target, handler) { | |||
const debugid = target.tagName + '#' + target.id; | |||
user().assert(target.id && target.id.substring(0, 4) == 'amp-', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(1) This should be a dev().assert()
(2) Why not also check here supportedActions
? This way you get an early notification as a developer that an element (at least) must be whitelisted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -33,6 +33,10 @@ const ACTION_QUEUE_ = '__AMP_ACTION_QUEUE__'; | |||
/** @const {string} */ | |||
const DEFAULT_METHOD_ = 'activate'; | |||
|
|||
/** @const {!Object<string,!Array<string>>} */ | |||
const ELEMENTS_ACTIONS_MAP_ = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good for now, but just FYI: it'd be even enough to just check for element name - the most likely error is confusing element's for another. If this set goes out of control - we can simply move the assertion for "unknown action" into the form's action handler itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack
a2b7c88
to
1ff2550
Compare
@dvoytenko added tests and addressed comments PTAL 👀 |
this.handleSubmit_(); | ||
} | ||
} | ||
|
||
/** @private */ | ||
installSubmitHandler_() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes 😄 I thought I did. Will do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
2a9ca40
to
1cc2699
Compare
LGTM |
Implements #4272
Play with the demo at the bottom of the page here. Make sure to toggle both
amp-form
andform-submit
experiments.