diff --git a/extensions/amp-autocomplete/0.1/amp-autocomplete.js b/extensions/amp-autocomplete/0.1/amp-autocomplete.js index 0af3029af4c5..f2efc7f27767 100644 --- a/extensions/amp-autocomplete/0.1/amp-autocomplete.js +++ b/extensions/amp-autocomplete/0.1/amp-autocomplete.js @@ -220,14 +220,14 @@ export class AmpAutocomplete extends AMP.BaseElement { this.viewport_ = null; /** @private {boolean} */ - this.interacted_ = false; + this.hasFetchedInitialData_ = false; /** * To ensure that we provide an accessible experience, * the suggestion container must have a unique ID. * In case the autocomplete doesn't have an ID we use a * random number to ensure uniqueness. - @private {number|string} + @private {number|string} */ this.containerId_ = element.id ? element.id @@ -499,6 +499,10 @@ export class AmpAutocomplete extends AMP.BaseElement { // Disable autofill in browsers. this.inputElement_.setAttribute('autocomplete', 'off'); + if (this.element.hasAttribute('prefetch')) { + this.checkFirstInteractionAndMaybeFetchData_(); + } + // Register event handlers. this.inputElement_.addEventListener( 'touchstart', @@ -687,9 +691,10 @@ export class AmpAutocomplete extends AMP.BaseElement { */ autocomplete_(data, opt_input = '') { this.clearAllItems_(); - if (opt_input.length < this.minChars_ || !data) { + if (!data || opt_input.length < this.minChars_) { return Promise.resolve(); - } else if (this.isSsr_) { + } + if (this.isSsr_) { return hasOwn(data, 'html') ? this.renderResults_( data, @@ -697,9 +702,8 @@ export class AmpAutocomplete extends AMP.BaseElement { opt_input ) : Promise.resolve(); - } else { - return this.filterDataAndRenderResults_(data, opt_input); } + return this.filterDataAndRenderResults_(data, opt_input); } /** @@ -990,10 +994,10 @@ export class AmpAutocomplete extends AMP.BaseElement { * @private */ checkFirstInteractionAndMaybeFetchData_() { - if (this.interacted_ || !this.element.hasAttribute('src')) { + if (this.hasFetchedInitialData_ || !this.element.hasAttribute('src')) { return Promise.resolve(); } - this.interacted_ = true; + this.hasFetchedInitialData_ = true; return this.getRemoteData_().then( (remoteData) => { this.sourceData_ = remoteData; diff --git a/extensions/amp-autocomplete/0.1/test/test-amp-autocomplete-init.js b/extensions/amp-autocomplete/0.1/test/test-amp-autocomplete-init.js index 2329d915d1f9..52f8619974a1 100644 --- a/extensions/amp-autocomplete/0.1/test/test-amp-autocomplete-init.js +++ b/extensions/amp-autocomplete/0.1/test/test-amp-autocomplete-init.js @@ -318,6 +318,27 @@ describes.realWin( }); }); + it('should prefetch remote data if prefetch attribute is specified', () => { + let impl; + return getAutocomplete( + { + 'prefetch': '', // boolean attribute, presence means prefetch is enabled. + 'src': 'https://examples.com/json', + 'filter': 'substring', + }, + '{}', + false + ) + .then((ampAutocomplete) => { + impl = ampAutocomplete.implementation_; + expect(impl.hasFetchedInitialData_).to.be.false; + return ampAutocomplete.layoutCallback(); + }) + .then(() => { + expect(impl.hasFetchedInitialData_).to.be.true; + }); + }); + it('should not fetch remote data when specified in src and using nested items property before first user interactino', () => { const data = { deeply: { diff --git a/extensions/amp-autocomplete/0.1/test/validator-amp-autocomplete.html b/extensions/amp-autocomplete/0.1/test/validator-amp-autocomplete.html index 4289b45b0e7b..d87aed8335ea 100644 --- a/extensions/amp-autocomplete/0.1/test/validator-amp-autocomplete.html +++ b/extensions/amp-autocomplete/0.1/test/validator-amp-autocomplete.html @@ -588,5 +588,23 @@ + + + + + + + + + + diff --git a/extensions/amp-autocomplete/0.1/test/validator-amp-autocomplete.out b/extensions/amp-autocomplete/0.1/test/validator-amp-autocomplete.out index f09c66a63e68..251c285df43e 100644 --- a/extensions/amp-autocomplete/0.1/test/validator-amp-autocomplete.out +++ b/extensions/amp-autocomplete/0.1/test/validator-amp-autocomplete.out @@ -609,5 +609,25 @@ amp-autocomplete/0.1/test/validator-amp-autocomplete.html:580:4 The attribute 'f amp-autocomplete/0.1/test/validator-amp-autocomplete.html:588:4 The attribute 'src' in tag 'amp-autocomplete' is missing or incorrect, but required by attribute 'query'. (see https://amp.dev/documentation/components/amp-autocomplete/) | | +| +| +| +| +| +| +| +| > ^~~~~~~~~ +amp-autocomplete/0.1/test/validator-amp-autocomplete.html:602:4 The attribute 'prefecth' may not appear in tag 'amp-autocomplete'. (see https://amp.dev/documentation/components/amp-autocomplete/) +| filter="prefix" +| src="https://data.com/articles.json?ref=CANONICAL_URL" +| prefecth +| > +| +| | | diff --git a/extensions/amp-autocomplete/amp-autocomplete.md b/extensions/amp-autocomplete/amp-autocomplete.md index 21cbb87675e4..fa9d2c55543c 100644 --- a/extensions/amp-autocomplete/amp-autocomplete.md +++ b/extensions/amp-autocomplete/amp-autocomplete.md @@ -198,6 +198,10 @@ Specifies the key to the data array within the JSON response. Nested keys can be Whether the amp-autocomplete should autosuggest on the full user input or only a triggered substring of the user input. By default when the attribute is absent, suggestions will be based on the full user input. The attribute cannot have an empty value but must take a single character token, i.e. @ which activates the autocomplete behavior. For example, if inline="@" then user input of hello will not retrieve suggestions but a user input of hello @abc might trigger options filtered on the substring abc. Currently triggered substrings are delimited on whitespace characters, however this is subject to change in the future. +### `prefetch` + +Include the `prefetch` attribute to prefetch remote data to improve responsiveness for users. Requires `src` to be specified. + ## Events ### `select` diff --git a/extensions/amp-autocomplete/validator-amp-autocomplete.protoascii b/extensions/amp-autocomplete/validator-amp-autocomplete.protoascii index 4f4cbdcfb421..55b28076d13f 100644 --- a/extensions/amp-autocomplete/validator-amp-autocomplete.protoascii +++ b/extensions/amp-autocomplete/validator-amp-autocomplete.protoascii @@ -65,6 +65,7 @@ tags: { # attrs: { name: "max-entries" } attrs: { name: "max-items" } attrs: { name: "min-characters" } + attrs: { name: "prefetch" } attrs: { name: "query" trigger: { @@ -107,6 +108,7 @@ tags: { # attrs: { name: "items" } attrs: { name: "max-items" } attrs: { name: "min-characters" } + attrs: { name: "prefetch" } attrs: { name: "query" trigger: {