diff --git a/packages/elements/src/create-custom-element.ts b/packages/elements/src/create-custom-element.ts index 2e1069840f..8159c43b3d 100644 --- a/packages/elements/src/create-custom-element.ts +++ b/packages/elements/src/create-custom-element.ts @@ -144,8 +144,9 @@ export function createCustomElement
( // // TODO(andrewseguin): Add e2e tests that cover cases where the constructor isn't called. For // now this is tested using a Google internal test suite. - if (this._ngElementStrategy === null) { - const strategy = this._ngElementStrategy = strategyFactory.create(this.injector); + if (!this._ngElementStrategy) { + const strategy = this._ngElementStrategy = + strategyFactory.create(this.injector || config.injector); // Collect pre-existing values on the element to re-apply through the strategy. const preExistingValues = @@ -173,12 +174,10 @@ export function createCustomElement
( return this._ngElementStrategy!; } - private readonly injector: Injector; - private _ngElementStrategy: NgElementStrategy|null = null; + private _ngElementStrategy?: NgElementStrategy; - constructor(injector?: Injector) { + constructor(private readonly injector?: Injector) { super(); - this.injector = injector || config.injector; } attributeChangedCallback( diff --git a/packages/elements/test/create-custom-element_spec.ts b/packages/elements/test/create-custom-element_spec.ts index 3551084d72..3f9dcd5eaf 100644 --- a/packages/elements/test/create-custom-element_spec.ts +++ b/packages/elements/test/create-custom-element_spec.ts @@ -73,6 +73,26 @@ if (browserDetection.supportsCustomElements) { expect(strategy.getInputValue('barBar')).toBe('value-barbar'); }); + it('should work even if when the constructor is not called (due to polyfill)', () => { + // Some polyfills (e.g. `document-register-element`) do not call the constructor of custom + // elements. Currently, all the constructor does is initialize the `injector` property. This + // test simulates not having called the constructor by "unsetting" the property. + // + // NOTE: + // If the constructor implementation changes in the future, this test needs to be adjusted + // accordingly. + const element = new NgElementCtor(injector); + delete (element as any).injector; + + element.setAttribute('foo-foo', 'value-foo-foo'); + element.setAttribute('barbar', 'value-barbar'); + element.connectedCallback(); + + expect(strategy.connectedElement).toBe(element); + expect(strategy.getInputValue('fooFoo')).toBe('value-foo-foo'); + expect(strategy.getInputValue('barBar')).toBe('value-barbar'); + }); + it('should listen to output events after connected', () => { const element = new NgElementCtor(injector); element.connectedCallback(); @@ -260,7 +280,14 @@ if (browserDetection.supportsCustomElements) { class TestStrategyFactory implements NgElementStrategyFactory { testStrategy = new TestStrategy(); - create(): NgElementStrategy { + create(injector: Injector): NgElementStrategy { + // Although not used by the `TestStrategy`, verify that the injector is provided. + if (!injector) { + throw new Error( + 'Expected injector to be passed to `TestStrategyFactory#create()`, but received ' + + `value of type ${typeof injector}: ${injector}`); + } + return this.testStrategy; } }