Skip to content

Commit

Permalink
fix(elements): correctly handle getting/setting properties before con…
Browse files Browse the repository at this point in the history
…necting the element (#36114)

`createCustomElements()` creates some getters/setters for properties
corresponding to component inputs that delegate to the
`NgElementStrategy`. However, it is not guaranteed that the element's
`NgElementStrategy` will have been created when these getters/setters
are called, because some polyfills (e.g. `document-register-element`) do
not call the constructor.

Previously, trying to get/set input properties before connecting the
element to the DOM (via `connectedCallback()`) would fail due to
`NgElementStrategy` not being created.

This commit ensures that the `NgElementStrategy` is always created
before used inside the input property getters/setters (similar to how it
is done for other methods of `NgElement`).

Mentioned in https://github.com/angular/angular/pull/31416/files#r300326698.

PR Close #36114
  • Loading branch information
gkalpak authored and subratpalhar92 committed Aug 15, 2020
1 parent c30fae8 commit 89b3fea
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 0 deletions.
6 changes: 6 additions & 0 deletions packages/elements/src/create-custom-element.ts
Expand Up @@ -187,9 +187,15 @@ export function createCustomElement<P>(
inputs.map(({propName}) => propName).forEach(property => {
Object.defineProperty(NgElementImpl.prototype, property, {
get: function() {
if (!this.ngElementStrategy) {
this.ngElementStrategy = strategyFactory.create(config.injector);
}
return this.ngElementStrategy.getInputValue(property);
},
set: function(newValue: any) {
if (!this.ngElementStrategy) {
this.ngElementStrategy = strategyFactory.create(config.injector);
}
this.ngElementStrategy.setInputValue(property, newValue);
},
configurable: true,
Expand Down
16 changes: 16 additions & 0 deletions packages/elements/test/create-custom-element_spec.ts
Expand Up @@ -94,6 +94,22 @@ if (browserDetection.supportsCustomElements) {
expect(strategy.inputs.get('fooFoo')).toBe('foo-foo-value');
expect(strategy.inputs.get('barBar')).toBe('barBar-value');
});

it('should properly handle getting/setting properties on the element even if the constructor is not called',
() => {
// Create a custom element while ensuring that the `NgElementStrategy` is not created
// inside the constructor. This is done to emulate the behavior of some polyfills that do
// not call the constructor.
strategyFactory.create = () => undefined as unknown as NgElementStrategy;
const element = new NgElementCtor(injector);
strategyFactory.create = TestStrategyFactory.prototype.create;

element.fooFoo = 'foo-foo-value';
element.barBar = 'barBar-value';

expect(strategy.inputs.get('fooFoo')).toBe('foo-foo-value');
expect(strategy.inputs.get('barBar')).toBe('barBar-value');
});
});
}

Expand Down

0 comments on commit 89b3fea

Please sign in to comment.