diff --git a/change/@microsoft-fast-element-47b69b7f-3b12-4084-b343-e7ee172f5539.json b/change/@microsoft-fast-element-47b69b7f-3b12-4084-b343-e7ee172f5539.json new file mode 100644 index 00000000000..bc91855aa59 --- /dev/null +++ b/change/@microsoft-fast-element-47b69b7f-3b12-4084-b343-e7ee172f5539.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "refactor: move template/style resolution to lazy getter", + "packageName": "@microsoft/fast-element", + "email": "roeisenb@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/web-components/fast-element/docs/api-report.md b/packages/web-components/fast-element/docs/api-report.md index 7bdd2112ad4..20d9abd0d0d 100644 --- a/packages/web-components/fast-element/docs/api-report.md +++ b/packages/web-components/fast-element/docs/api-report.md @@ -160,7 +160,7 @@ export class Controller extends PropertyChangeNotifier { // @internal constructor(element: HTMLElement, definition: FASTElementDefinition); addBehaviors(behaviors: ReadonlyArray>): void; - addStyles(styles: ElementStyles | HTMLStyleElement): void; + addStyles(styles: ElementStyles | HTMLStyleElement | null | undefined): void; readonly definition: FASTElementDefinition; readonly element: HTMLElement; emit(type: string, detail?: any, options?: Omit): void | boolean; @@ -170,7 +170,7 @@ export class Controller extends PropertyChangeNotifier { onConnectedCallback(): void; onDisconnectedCallback(): void; removeBehaviors(behaviors: ReadonlyArray>, force?: boolean): void; - removeStyles(styles: ElementStyles | HTMLStyleElement): void; + removeStyles(styles: ElementStyles | HTMLStyleElement | null | undefined): void; get styles(): ElementStyles | null; set styles(value: ElementStyles | null); get template(): ElementViewTemplate | null; diff --git a/packages/web-components/fast-element/src/components/controller.ts b/packages/web-components/fast-element/src/components/controller.ts index 5f48328ed1d..6730134b29f 100644 --- a/packages/web-components/fast-element/src/components/controller.ts +++ b/packages/web-components/fast-element/src/components/controller.ts @@ -77,11 +77,24 @@ export class Controller extends PropertyChangeNotifier { * @remarks * This value can only be accurately read after connect but can be set at any time. */ - get template(): ElementViewTemplate | null { + public get template(): ElementViewTemplate | null { + // 1. Template overrides take top precedence. + if (this._template === null) { + const definition = this.definition; + + if ((this.element as any).resolveTemplate) { + // 2. Allow for element instance overrides next. + this._template = (this.element as any).resolveTemplate(); + } else if (definition.template) { + // 3. Default to the static definition. + this._template = definition.template ?? null; + } + } + return this._template; } - set template(value: ElementViewTemplate | null) { + public set template(value: ElementViewTemplate | null) { if (this._template === value) { return; } @@ -98,11 +111,24 @@ export class Controller extends PropertyChangeNotifier { * @remarks * This value can only be accurately read after connect but can be set at any time. */ - get styles(): ElementStyles | null { + public get styles(): ElementStyles | null { + // 1. Styles overrides take top precedence. + if (this._styles === null) { + const definition = this.definition; + + if ((this.element as any).resolveStyles) { + // 2. Allow for element instance overrides next. + this._styles = (this.element as any).resolveStyles(); + } else if (definition.styles) { + // 3. Default to the static definition. + this._styles = definition.styles ?? null; + } + } + return this._styles; } - set styles(value: ElementStyles | null) { + public set styles(value: ElementStyles | null) { if (this._styles === value) { return; } @@ -113,7 +139,7 @@ export class Controller extends PropertyChangeNotifier { this._styles = value; - if (!this.needsInitialization && value !== null) { + if (!this.needsInitialization) { this.addStyles(value); } } @@ -165,7 +191,11 @@ export class Controller extends PropertyChangeNotifier { * Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot. * @param styles - The styles to add. */ - public addStyles(styles: ElementStyles | HTMLStyleElement): void { + public addStyles(styles: ElementStyles | HTMLStyleElement | null | undefined): void { + if (!styles) { + return; + } + const target = getShadowRoot(this.element) || ((this.element.getRootNode() as any) as StyleTarget); @@ -186,7 +216,13 @@ export class Controller extends PropertyChangeNotifier { * Removes styles from this element. Providing an HTMLStyleElement will detach the element instance from the shadowRoot. * @param styles - the styles to remove. */ - public removeStyles(styles: ElementStyles | HTMLStyleElement): void { + public removeStyles( + styles: ElementStyles | HTMLStyleElement | null | undefined + ): void { + if (!styles) { + return; + } + const target = getShadowRoot(this.element) || ((this.element.getRootNode() as any) as StyleTarget); @@ -381,41 +417,8 @@ export class Controller extends PropertyChangeNotifier { this.boundObservables = null; } - const definition = this.definition; - - // 1. Template overrides take top precedence. - if (this._template === null) { - if ((this.element as any).resolveTemplate) { - // 2. Allow for element instance overrides next. - this._template = (this.element as any).resolveTemplate(); - } else if (definition.template) { - // 3. Default to the static definition. - this._template = definition.template ?? null; - } - } - - // If we have a template after the above process, render it. - // If there's no template, then the element author has opted into - // custom rendering and they will managed the shadow root's content themselves. - if (this._template !== null) { - this.renderTemplate(this._template); - } - - // 1. Styles overrides take top precedence. - if (this._styles === null) { - if ((this.element as any).resolveStyles) { - // 2. Allow for element instance overrides next. - this._styles = (this.element as any).resolveStyles(); - } else if (definition.styles) { - // 3. Default to the static definition. - this._styles = definition.styles ?? null; - } - } - - // If we have styles after the above process, add them. - if (this._styles !== null) { - this.addStyles(this._styles); - } + this.renderTemplate(this.template); + this.addStyles(this.styles); this.needsInitialization = false; }