Skip to content

Commit

Permalink
Merge pull request #904 from Polymer/asyncQuery
Browse files Browse the repository at this point in the history
Adds `@queryAsync` decorator
  • Loading branch information
Steve Orvell authored Feb 28, 2020
2 parents 628711a + ec0f2dc commit 672e457
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
* The value returned by `render` is always rendered, even if it isn't a `TemplateResult`. ([#712](https://github.com/Polymer/lit-element/issues/712)

### Added
* Added `@queryAsync(selector)` decorator which returns a Promise that resolves to the result of querying for the given selector after the element's `updateComplete` Promise resolves ([#903](https://github.com/Polymer/lit-element/issues/903)).
* Added `enableUpdating()` to `UpdatingElement` to enable customizing when updating is enabled [#860](https://github.com/Polymer/lit-element/pull/860).
* Added `queryAssignedNodes(slotName, flatten)` to enable querying assignedNodes for a given slot [#860](https://github.com/Polymer/lit-element/pull/860).
* Added `getStyles()` to `LitElement` to allow hooks into style gathering for component sets [#866](https://github.com/Polymer/lit-element/pull/866).
Expand Down
55 changes: 55 additions & 0 deletions src/lib/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export function internalProperty(options?: InternalPropertyDeclaration) {
* `;
* }
* }
*
*/
export function query(selector: string) {
return (protoOrDescriptor: Object|ClassElement,
Expand All @@ -225,6 +226,60 @@ export function query(selector: string) {
};
}

// Note, in the future, we may extend this decorator to support the use case
// where the queried element may need to do work to become ready to interact
// with (e.g. load some implementation code). If so, we might elect to
// add a second argument defining a function that can be run to make the
// queried element loaded/updated/ready.
/**
* A property decorator that converts a class property into a getter that
* returns a promise that resolves to the result of a querySelector on the
* element's renderRoot done after the element's `updateComplete` promise
* resolves. When the queried property may change with element state, this
* decorator can be used instead of requiring users to await the
* `updateComplete` before accessing the property.
*
* @param selector A DOMString containing one or more selectors to match.
*
* See: https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
*
* @example
*
* class MyElement {
* @queryAsync('#first')
* first;
*
* render() {
* return html`
* <div id="first"></div>
* <div id="second"></div>
* `;
* }
* }
*
* // external usage
* async doSomethingWithFirst() {
* (await aMyElement.first).doSomething();
* }
*/
export function queryAsync(selector: string) {
return (protoOrDescriptor: Object|ClassElement,
// tslint:disable-next-line:no-any decorator
name?: PropertyKey): any => {
const descriptor = {
async get(this: LitElement) {
await this.updateComplete;
return this.renderRoot.querySelector(selector);
},
enumerable: true,
configurable: true,
};
return (name !== undefined) ?
legacyQuery(descriptor, protoOrDescriptor as Object, name) :
standardQuery(descriptor, protoOrDescriptor as ClassElement);
};
}

/**
* A property decorator that converts a class property into a getter
* that executes a querySelectorAll on the element's renderRoot.
Expand Down
43 changes: 42 additions & 1 deletion src/test/lib/decorators_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/

import {eventOptions, property} from '../../lib/decorators.js';
import {customElement, html, LitElement, PropertyValues, query, queryAll, queryAssignedNodes} from '../../lit-element.js';
import {customElement, html, LitElement, PropertyValues, query, queryAll, queryAssignedNodes, queryAsync} from '../../lit-element.js';
import {generateElementName} from '../test-helpers.js';

const flush =
Expand Down Expand Up @@ -460,6 +460,47 @@ suite('decorators', () => {
});
});

suite('@queryAsync', () => {

@customElement(generateElementName() as keyof HTMLElementTagNameMap)
class C extends LitElement {
@queryAsync('#blah') blah!: Promise<HTMLDivElement>;

@queryAsync('span') nope!: Promise<HTMLSpanElement|null>;

@property()
foo = false;

render() {
return html`
<div>Not this one</div>
${this.foo ?
html`<div id="blah" foo>This one</div>` :
html`<div id="blah">This one</div>` }
`;
}
}

test('returns an element when it exists after update', async () => {
const c = new C();
container.appendChild(c);
let div = await c.blah;
assert.instanceOf(div, HTMLDivElement);
assert.isFalse(div.hasAttribute('foo'));
c.foo = true;
div = await c.blah;
assert.instanceOf(div, HTMLDivElement);
assert.isTrue(div.hasAttribute('foo'));
});

test('returns null when no match', async () => {
const c = new C();
container.appendChild(c);
const span = await c.nope;
assert.isNull(span);
});
});

suite('@eventOptions', () => {
test('allows capturing listeners', async function() {
if (!supportsOptions) {
Expand Down

0 comments on commit 672e457

Please sign in to comment.