fix(elements): correctly handle getting/setting properties before connecting 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
This commit is contained in:
George Kalpakas 2020-05-20 11:10:20 +03:00 committed by Kara Erickson
parent 4c30aa8343
commit 327980bf49
2 changed files with 22 additions and 0 deletions

View File

@ -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,

View File

@ -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');
});
});
}