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

amp-bind: Support changing src on amp-list #8735

Merged
merged 16 commits into from Apr 21, 2017

Conversation

kmh287
Copy link
Contributor

@kmh287 kmh287 commented Apr 12, 2017

This PR introduces changes to allow binding to src on amp-list. When the value of the src attribute changes, the amp-list element performs another XHR to get new list data and replaces the current data with the rendered results of the XHR.

Reading order:

  1. Changes to amp-list
    • amp-list.js for implementation changes
    • test-amp-list.js for updated tests
    • validator-amp-list.protoascii for validator changes
  2. Changes to bind
    • bind-validator.js for adding a new rule for amp-list and its src attribute
    • test-bind-integration.js for integration test changes
    • amp-bind-integrations.html for integration test fixture changes
  3. Bind example
    • app.js To create JSON endpoints for two sets of list data
    • list.html For an example of amp-list with a changeable src
  4. Updated Documentation
    • amp-bind.md To document addition of amp-list and its src property

Partial for #8700

/to @choumx

<strong>Unit Price</strong>: ${{unitPrice}}
</template>
</amp-list>
</body>

Choose a reason for hiding this comment

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

Can you build a paginated sorting/filtering demo as a template for ampbyexample?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure, let's discuss more offline

/** @override */
mutatedAttributesCallback(mutations) {
const srcMutation = mutations['src'];
if (srcMutation) {

Choose a reason for hiding this comment

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

mutations['src'] !== undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -82,6 +114,11 @@ export class AmpList extends AMP.BaseElement {
}
});
}

/** @return {Promise} */

Choose a reason for hiding this comment

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

@visibleForTesting

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@kmh287
Copy link
Contributor Author

kmh287 commented Apr 14, 2017

/to @aghassemi

<button on="tap:AMP.setState({listSrc:'/list/fruit-data/get'})">Show Fruit</button>
<button on="tap:AMP.setState({listSrc:'/list/vegetable-data/get'})">Show Vegetables</button>

<amp-list layout="responsive" src="/list/fruit-data/get" [src]="listSrc || '/list/fruit-data/get'" width="600" height="600">
Copy link
Contributor

Choose a reason for hiding this comment

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

why is the || '/list/fruit-data/get' part needed here?

Copy link
Contributor Author

@kmh287 kmh287 Apr 17, 2017

Choose a reason for hiding this comment

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

We're encouraging developers to ensure that the expression for [property] initially evaluates to the value for property. This example is simple enough that there's no risk of unexpected behavior when bind first evaluates the expression, but it doesn't hurt to include the OR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it. Thanks for the explanation.

if (srcMutation != undefined) {
const p = this.populateList_();
if (getMode().test) {
this.mutationPromise_ = p;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we instead try to get what we need by stubbing this.populateList_ in the test instead`?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

100% agree that this code isn't ideal. I try to avoid depending on private properties/methods when writing unit tests, hence the promise and the visibleForTesting getter. If you think that changing it as proposed will make this easier to read / work with let me know and I'll make the change. 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Unit tests accessing private properties/methods is definitely fine, I prefer that over exporting specific methods/properties just for testing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -66,6 +97,7 @@ export class AmpList extends AMP.BaseElement {
* @private
*/
rendered_(elements) {
removeChildren(this.container_);
Copy link
Contributor

Choose a reason for hiding this comment

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

since the list can now become very dynamic, can you please add setAttribute('aria-live', 'polite'); to this.element in the buildCallback. That would let screen readers announce changes when list refreshes.

Copy link
Contributor Author

@kmh287 kmh287 Apr 17, 2017

Choose a reason for hiding this comment

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

Good idea, and that's on the element, not the container, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't matter for screen-readers, element is probably better since it would allow page authors to override it (maybe they want aria-live='assertive' for certain lists).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor

@aghassemi aghassemi left a comment

Choose a reason for hiding this comment

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

lgtm

@@ -283,6 +283,7 @@ Only binding to the following components and attributes are allowed:
| `<amp-carousel type=slides>` | `[slide]`<sup>1</sup> | Changes the currently displayed slide index. [See an example](https://ampbyexample.com/advanced/image_galleries_with_amp-carousel/#linking-carousels-with-amp-bind).
| `<amp-iframe>` | `[src]` | Changes the iframe's source URL. |
| `<amp-img>` | `[alt]`<br>`[attribution]`<br>`[src]`<br>`[srcset]` | See corresponding [amp-img attributes](https://www.ampproject.org/docs/reference/components/media/amp-img#attributes). |
| `<amp-list>` | `[src]` | Change the list's source URL. |

Choose a reason for hiding this comment

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

How about "Fetches JSON from the new URL and re-renders while discarding old content."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tweaked it a bit but otherwise like the parallelism with amp-state's description 👍


/** @override */
mutatedAttributesCallback(mutations) {
const srcMutation = mutations['src'];

Choose a reason for hiding this comment

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

Nit: This can be inlined below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea

Copy link
Contributor Author

@kmh287 kmh287 left a comment

Choose a reason for hiding this comment

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

Thanks for the review!

@@ -283,6 +283,7 @@ Only binding to the following components and attributes are allowed:
| `<amp-carousel type=slides>` | `[slide]`<sup>1</sup> | Changes the currently displayed slide index. [See an example](https://ampbyexample.com/advanced/image_galleries_with_amp-carousel/#linking-carousels-with-amp-bind).
| `<amp-iframe>` | `[src]` | Changes the iframe's source URL. |
| `<amp-img>` | `[alt]`<br>`[attribution]`<br>`[src]`<br>`[srcset]` | See corresponding [amp-img attributes](https://www.ampproject.org/docs/reference/components/media/amp-img#attributes). |
| `<amp-list>` | `[src]` | Change the list's source URL. |
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tweaked it a bit but otherwise like the parallelism with amp-state's description 👍


/** @override */
mutatedAttributesCallback(mutations) {
const srcMutation = mutations['src'];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea

@kmh287 kmh287 merged commit 18733f7 into ampproject:master Apr 21, 2017
DarXector pushed a commit to DarXector/amphtml that referenced this pull request Apr 25, 2017
mrjoro pushed a commit to mrjoro/amphtml that referenced this pull request Apr 28, 2017
KenneyE pushed a commit to spotxchange/amphtml that referenced this pull request May 3, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants