-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
✨PWA: Expose amp-bind getState and setState #25011
Conversation
Expose shadowDoc.getState and shadowDoc.setState methods for PWAs.
@choumx Initial thoughts welcome. I'm actually blocked on testing .setState because this promise seems to hang (at least in localhost testing): amphtml/extensions/amp-bind/0.1/bind-impl.js Line 1024 in bcdd618
Oh, I see, ww.js isn't lazy building: |
src/runtime.js
Outdated
* a copy of the value of a state | ||
*/ | ||
getState: name => { | ||
return Services.bindForDocOrNull(shadowRoot).then(bind => { |
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.
For pages that do not use amp-bind
, this promise never resolves. I don't think it's a problem, but wanted to make sure this received consideration.
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.
Hm, it should resolve with null
on pages that are missing the extension script in the doc head.
src/runtime.js
Outdated
* @return {Promise} - Resolves after state and history have been updated | ||
*/ | ||
setState: state => { | ||
return Services.bindForDocOrNull(shadowRoot).then(bind => { |
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.
same here
src/runtime.js
Outdated
*/ | ||
getState: name => { | ||
return Services.bindForDocOrNull(shadowRoot).then(bind => { | ||
return bind && bind.getStateCopy(name); |
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 should probably be changed to:
return bind ? bind.getStateCopy(name) : Promise.resolve();
return Services.bindForDocOrNull(shadowRoot).then(bind => { | ||
return ( | ||
(bind && bind.setStateAndUpdateHistory(state)) || Promise.resolve() | ||
); |
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 would be more readable as:
return bind ? bind.setStateAndUpdateHistory(state) : Promise.resolve();
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.
Thanks for taking this on!
spec/amp-shadow-doc.md
Outdated
@@ -94,6 +94,8 @@ Both `AMP.attachShadowDoc` and `AMP.attachShadowDocAsStream` return a `ShadowDoc | |||
- `shadowDoc.setVisibilityState()` - changes the visibility state of the AMP document. | |||
- `shadowDoc.postMessage()` and `shadowDoc.onMessage()` - can be used to message with the AMP document. | |||
- `shadowDoc.close()` - closes the AMP document and frees the resources. | |||
- `shadowDoc.bind.getState(expr)` - Get an `amp-bind` state from the AMP document using expression syntax | |||
- `shadowDoc.bind.setState(obj)` - Deep merge an object into the AMP document's global `amp-bind` state |
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.
IMO it's fine to not namespace this under .bind
since we don't namespace the on="tap:AMP.setState(...)"
action either.
spec/amp-shadow-doc.md
Outdated
@@ -94,6 +94,8 @@ Both `AMP.attachShadowDoc` and `AMP.attachShadowDocAsStream` return a `ShadowDoc | |||
- `shadowDoc.setVisibilityState()` - changes the visibility state of the AMP document. | |||
- `shadowDoc.postMessage()` and `shadowDoc.onMessage()` - can be used to message with the AMP document. | |||
- `shadowDoc.close()` - closes the AMP document and frees the resources. | |||
- `shadowDoc.bind.getState(expr)` - Get an `amp-bind` state from the AMP document using expression syntax |
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.
Let's mention that expr
is a JSON expression string e.g. "foo.bar".
extensions/amp-bind/0.1/bind-impl.js
Outdated
* @param {string} expr | ||
* @return {(?JsonObject|string|undefined)} | ||
*/ | ||
getStateCopy(expr) { |
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.
Nit: Let's just call this getState()
. There shouldn't be a use case for returning a non-copy.
extensions/amp-bind/0.1/bind-impl.js
Outdated
// Support expressions | ||
if (typeof state === 'string') { | ||
// Emulate UIEvent 'click' | ||
return this.pushStateWithExpression( |
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.
A bit confusing if this API can both "set" and "push" state. I think it's worth separating into two APIs.
Though for the sake of keeping this PR small, can we remove this case?
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.
To make sure I understand, are you suggesting that we do not update history with the shadowDoc.setState()
method?
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.
I mean removing this case where passing a string calls "push" instead of "set". The former appends a new entry onto the history stack.
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.
Or maybe you just meant to call setStateWithExpression
here?
extensions/amp-bind/0.1/bind-impl.js
Outdated
this.history_.replace(data); | ||
} | ||
}); | ||
return this.setStatePromise_; |
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.
Would be nice to de-dupe this code snippet with setStateWithExpression
. Feel free to create a new private helper method or just drop a TODO(choumx)
here for me.
Thank you for the review! I believe all review comments are resolved in 1a1582e. |
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 good overall.
/cc @dvoytenko FYI a new shadow API for amp-bind.
src/runtime.js
Outdated
// Emulate UIEvent 'click' | ||
return bind.setStateWithExpression( | ||
/** @type {string} */ (state), | ||
/** @type {!JsonObject} */ ({event: 1}) |
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.
Is {event: 1}
useful 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.
I haven't dug too deep into how the scope parameter is used, but at least an empty object needs to be passed. I opted to mimic the object that is passed by a single click event 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.
Empty object will suffice then. :)
It's used for things like on="submit-success: AMP.setState({foo: event.response})"
, but there's no real "event context" in this case.
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.
API LGTM
Great contribution! |
* PWA: Expose getState and setState Expose shadowDoc.getState and shadowDoc.setState methods for PWAs. * PWA: Remove inconsistent return value for setState * Minor cleanup * Fix check-types * Add micro documentation for PWA (get/set)State * Support PWA setState with expression syntax * Fixup types * Review updates and add tests * Fix check-types * Fix empty string return value from getState * Don't limit return type of getState * Fix unit test * Do not emulate event in setState * Really fix unit test
* PWA: Expose getState and setState Expose shadowDoc.getState and shadowDoc.setState methods for PWAs. * PWA: Remove inconsistent return value for setState * Minor cleanup * Fix check-types * Add micro documentation for PWA (get/set)State * Support PWA setState with expression syntax * Fixup types * Review updates and add tests * Fix check-types * Fix empty string return value from getState * Don't limit return type of getState * Fix unit test * Do not emulate event in setState * Really fix unit test
Allow PWAs to access
amp-bind
methods through theshadowDoc
instance created byAMP.attachShadowDoc()
:shadowDoc.getState(expr)
- Get anamp-bind
state from the AMP document using a JSON expression stringshadowDoc.setState(state)
- Deep merge an object or expression into the AMP document's globalamp-bind
stateFixes #24982