From 7603fa3aa86ea0c97655764606ba60f058c93bbe Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Jun 2024 16:49:10 +0200 Subject: [PATCH] Don't set hass to undefined in lovelace cards. (#21189) * Wait for hass and config before building the card * Don't use setter * Improve code readability * Use hasupdated * Rename build to load --- src/panels/lovelace/cards/hui-card.ts | 90 ++++++++++--------- .../lovelace/cards/hui-conditional-card.ts | 1 + .../lovelace/cards/hui-entity-filter-card.ts | 1 + src/panels/lovelace/cards/hui-stack-card.ts | 3 +- .../card-editor/hui-card-layout-editor.ts | 1 + src/panels/lovelace/sections/hui-section.ts | 1 + src/panels/lovelace/views/hui-view.ts | 1 + 7 files changed, 55 insertions(+), 43 deletions(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index a892d4764dcf..6249acaba095 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -1,4 +1,4 @@ -import { PropertyValueMap, PropertyValues, ReactiveElement } from "lit"; +import { PropertyValues, ReactiveElement } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../../common/dom/fire_event"; import { MediaQueriesListener } from "../../../common/dom/media_query"; @@ -23,29 +23,20 @@ declare global { @customElement("hui-card") export class HuiCard extends ReactiveElement { - @property({ attribute: false }) public hass?: HomeAssistant; - @property({ type: Boolean }) public preview = false; @property({ type: Boolean }) public isPanel = false; - set config(config: LovelaceCardConfig | undefined) { - if (!config) return; - if (config.type !== this._config?.type) { - this._buildElement(config); - } else if (config !== this.config) { - this._element?.setConfig(config); - fireEvent(this, "card-updated"); - } - this._config = config; - } + @property({ attribute: false }) public config?: LovelaceCardConfig; - @property({ attribute: false }) - public get config() { - return this._config; - } + @property({ attribute: false }) public hass?: HomeAssistant; - private _config?: LovelaceCardConfig; + public load() { + if (!this.config) { + throw new Error("Cannot build card without config"); + } + this._loadElement(this.config); + } private _element?: LovelaceCard; @@ -90,57 +81,76 @@ export class HuiCard extends ReactiveElement { return this._element?.getLayoutOptions?.() ?? {}; } - private _createElement(config: LovelaceCardConfig) { - const element = createCardElement(config); - element.hass = this.hass; - element.preview = this.preview; + private _loadElement(config: LovelaceCardConfig) { + this._element = createCardElement(config); + if (this.hass) { + this._element.hass = this.hass; + } + this._element.preview = this.preview; // For backwards compatibility - (element as any).editMode = this.preview; + (this._element as any).editMode = this.preview; // Update element when the visibility of the card changes (e.g. conditional card or filter card) - element.addEventListener("card-visibility-changed", (ev: Event) => { + this._element.addEventListener("card-visibility-changed", (ev: Event) => { ev.stopPropagation(); this._updateVisibility(); }); - element.addEventListener( + this._element.addEventListener( "ll-upgrade", (ev: Event) => { ev.stopPropagation(); - element.hass = this.hass; - element.preview = this.preview; + if (this.hass) { + this._element!.hass = this.hass; + } fireEvent(this, "card-updated"); }, { once: true } ); - element.addEventListener( + this._element.addEventListener( "ll-rebuild", (ev: Event) => { ev.stopPropagation(); - this._buildElement(config); + this._loadElement(config); fireEvent(this, "card-updated"); }, { once: true } ); - return element; - } - - private _buildElement(config: LovelaceCardConfig) { - this._element = this._createElement(config); - while (this.lastChild) { this.removeChild(this.lastChild); } this._updateVisibility(); } + protected willUpdate(changedProps: PropertyValues): void { + super.willUpdate(changedProps); + + if (!this._element) { + this.load(); + } + } + protected update(changedProps: PropertyValues) { super.update(changedProps); if (this._element) { + if (changedProps.has("config") && this.hasUpdated) { + const oldConfig = changedProps.get("config"); + if (this.config !== oldConfig && this.config) { + const typeChanged = this.config?.type !== oldConfig?.type; + if (typeChanged) { + this._loadElement(this.config); + } else { + this._element?.setConfig(this.config); + fireEvent(this, "card-updated"); + } + } + } if (changedProps.has("hass")) { try { - this._element.hass = this.hass; + if (this.hass) { + this._element.hass = this.hass; + } } catch (e: any) { - this._buildElement(createErrorCardConfig(e.message, null)); + this._loadElement(createErrorCardConfig(e.message, null)); } } if (changedProps.has("preview")) { @@ -149,18 +159,14 @@ export class HuiCard extends ReactiveElement { // For backwards compatibility (this._element as any).editMode = this.preview; } catch (e: any) { - this._buildElement(createErrorCardConfig(e.message, null)); + this._loadElement(createErrorCardConfig(e.message, null)); } } if (changedProps.has("isPanel")) { this._element.isPanel = this.isPanel; } } - } - protected willUpdate( - changedProps: PropertyValueMap | Map - ): void { if (changedProps.has("hass") || changedProps.has("preview")) { this._updateVisibility(); } diff --git a/src/panels/lovelace/cards/hui-conditional-card.ts b/src/panels/lovelace/cards/hui-conditional-card.ts index 9a57b3e5a343..630a7494c9b0 100644 --- a/src/panels/lovelace/cards/hui-conditional-card.ts +++ b/src/panels/lovelace/cards/hui-conditional-card.ts @@ -41,6 +41,7 @@ class HuiConditionalCard extends HuiConditionalBase implements LovelaceCard { element.hass = this.hass; element.preview = this.preview; element.config = cardConfig; + element.load(); return element; } diff --git a/src/panels/lovelace/cards/hui-entity-filter-card.ts b/src/panels/lovelace/cards/hui-entity-filter-card.ts index ca8372b4a7c0..d79e88ad0daf 100644 --- a/src/panels/lovelace/cards/hui-entity-filter-card.ts +++ b/src/panels/lovelace/cards/hui-entity-filter-card.ts @@ -249,6 +249,7 @@ export class HuiEntityFilterCard element.hass = this.hass; element.preview = this.preview; element.config = cardConfig; + element.load(); return element; } } diff --git a/src/panels/lovelace/cards/hui-stack-card.ts b/src/panels/lovelace/cards/hui-stack-card.ts index a308e368eb59..93bc5c4931f9 100644 --- a/src/panels/lovelace/cards/hui-stack-card.ts +++ b/src/panels/lovelace/cards/hui-stack-card.ts @@ -56,7 +56,7 @@ export abstract class HuiStackCard card.hass = this.hass; }); } - if (changedProperties.has("editMode")) { + if (changedProperties.has("preview")) { this._cards.forEach((card) => { card.preview = this.preview; }); @@ -69,6 +69,7 @@ export abstract class HuiStackCard element.hass = this.hass; element.preview = this.preview; element.config = cardConfig; + element.load(); return element; } diff --git a/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts b/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts index f9cda6e38bac..68c0487beee4 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts @@ -146,6 +146,7 @@ export class HuiCardLayoutEditor extends LitElement { this._defaultLayoutOptions = this._cardElement?.getElementLayoutOptions(); }); + this._cardElement.load(); this._defaultLayoutOptions = this._cardElement.getElementLayoutOptions(); } catch (err) { // eslint-disable-next-line no-console diff --git a/src/panels/lovelace/sections/hui-section.ts b/src/panels/lovelace/sections/hui-section.ts index b662072a13e4..07dc7a78f8af 100644 --- a/src/panels/lovelace/sections/hui-section.ts +++ b/src/panels/lovelace/sections/hui-section.ts @@ -64,6 +64,7 @@ export class HuiSection extends ReactiveElement { ev.stopPropagation(); this._cards = [...this._cards]; }); + element.load(); return element; } diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index 06df93b7465b..ea0bb5a89512 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -82,6 +82,7 @@ export class HUIView extends ReactiveElement { ev.stopPropagation(); this._cards = [...this._cards]; }); + element.load(); return element; }