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:
parent
4c30aa8343
commit
327980bf49
|
@ -187,9 +187,15 @@ export function createCustomElement<P>(
|
||||||
inputs.map(({propName}) => propName).forEach(property => {
|
inputs.map(({propName}) => propName).forEach(property => {
|
||||||
Object.defineProperty(NgElementImpl.prototype, property, {
|
Object.defineProperty(NgElementImpl.prototype, property, {
|
||||||
get: function() {
|
get: function() {
|
||||||
|
if (!this.ngElementStrategy) {
|
||||||
|
this.ngElementStrategy = strategyFactory.create(config.injector);
|
||||||
|
}
|
||||||
return this.ngElementStrategy.getInputValue(property);
|
return this.ngElementStrategy.getInputValue(property);
|
||||||
},
|
},
|
||||||
set: function(newValue: any) {
|
set: function(newValue: any) {
|
||||||
|
if (!this.ngElementStrategy) {
|
||||||
|
this.ngElementStrategy = strategyFactory.create(config.injector);
|
||||||
|
}
|
||||||
this.ngElementStrategy.setInputValue(property, newValue);
|
this.ngElementStrategy.setInputValue(property, newValue);
|
||||||
},
|
},
|
||||||
configurable: true,
|
configurable: true,
|
||||||
|
|
|
@ -94,6 +94,22 @@ if (browserDetection.supportsCustomElements) {
|
||||||
expect(strategy.inputs.get('fooFoo')).toBe('foo-foo-value');
|
expect(strategy.inputs.get('fooFoo')).toBe('foo-foo-value');
|
||||||
expect(strategy.inputs.get('barBar')).toBe('barBar-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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue