Skip to content

Commit

Permalink
Merge pull request #959 from Polymer/performUpdate
Browse files Browse the repository at this point in the history
Makes `performUpdate` possible to call synchronously
  • Loading branch information
Steve Orvell committed Apr 14, 2020
2 parents c8e182d + 8e64370 commit 4fb9206
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
<!-- ### Removed -->
<!-- ### Fixed -->

### Added
* The protected `performUpdate()` method may now be called to syncronously "flush" a pending update, for example via a property setter. Note, performing a synchronous update only updates the element and not any potentially pending descendants in the element's local DOM ([#959](https://github.com/Polymer/lit-element/issues/959)).

* Constructible stylesheets may now be provided directly as styles, in addition to using the `css` tagged template function ([#853](https://github.com/Polymer/lit-element/issues/853)).

## [2.3.1] - 2020-03-19

### Fixed
Expand Down
6 changes: 6 additions & 0 deletions src/lib/updating-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,12 @@ export abstract class UpdatingElement extends HTMLElement {
* ```
*/
protected performUpdate(): void|Promise<unknown> {
// Abort any update if one is not pending when this is called.
// This can happen if `performUpdate` is called early to "flush"
// the update.
if (!this._hasRequestedUpdate) {
return;
}
// Mixin instance properties once, if they exist.
if (this._instanceProperties) {
this._applyInstanceProperties();
Expand Down
78 changes: 78 additions & 0 deletions src/test/lib/updating-element_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,84 @@ suite('UpdatingElement', () => {
assert.deepEqual(el._observedZot2, {value: 'zot', oldValue: ''});
});

test('can customize properties to update synchronously', async () => {

interface MyPropertyDeclaration extends PropertyDeclaration {
sync: boolean;
}

@customElement(generateElementName())
class E extends UpdatingElement {

static getPropertyDescriptor(
name: PropertyKey,
key: string|symbol,
options: MyPropertyDeclaration) {
const defaultDescriptor = super.getPropertyDescriptor(name, key, options);
const setter = defaultDescriptor.set;
return Object.assign(defaultDescriptor, {
set(this: E, value: unknown) {
setter.call(this, value);
if (options.sync && this.hasUpdated && !this.isUpdating) {
(this as unknown as E).performUpdate();
}
}
});
}

isUpdating = false;

updateCount = 0;

performUpdate() {
// While it's dubious to have a computed property that's
// also settable but this just demonstrates it's possible.
this.isUpdating = true;
super.performUpdate();
this.isUpdating = false;
}

update(changedProperties: PropertyValues) {
this.zug = this.foo + 1;
super.update(changedProperties);
}

updated() {
this.updateCount++;
}

@property({type: Number, sync: true, reflect: true} as PropertyDeclaration)
foo = 5;

@property({type: Number, sync: true} as PropertyDeclaration)
zug = this.foo;

@property({})
bar = 'bar';

}

const el = new E();
container.appendChild(el);
await el.updateComplete;
el.foo = 10;
assert.equal(el.updateCount, 2);
el.foo = 1;
el.foo = 2;
assert.equal(el.updateCount, 4);
el.foo = 3;
await el.updateComplete;
assert.equal(el.updateCount, 5);
el.bar = 'bar2';
assert.equal(el.updateCount, 5);
await el.updateComplete;
assert.equal(el.updateCount, 6);
el.foo = 5;
assert.equal(el.updateCount, 7);
el.zug = 60;
assert.equal(el.updateCount, 8);
});

test('attribute-based property storage', async () => {
class E extends UpdatingElement {
_updateCount = 0;
Expand Down

0 comments on commit 4fb9206

Please sign in to comment.