У меня есть родительский компонент под названием AMS-Card , как ниже. Некоторый код удален, чтобы сохранить его коротким, но полная версия здесь < /p>
Визуально, собрать вместе, это выглядит так:
Когда вы нажимаете на катушку, он открывает модал с дополнительной информацией. Проблема, которая у меня возникает, заключается в том, что это запускает его для повторного рендеринга, что закрывает модал. Я предполагаю, что в этот момент он исходит от родительской AMS-карты, но я немного не уверен. Собственное право, и это никогда не закрывается из -за обновлений, поэтому я думаю, что, возможно, это как -то связано с повторением, и, возможно, некоторыми справочными столкновениями или что -то в этом роде. Я честно не уверен на этом этапе
амс-card.ts
@customElement(AMS_CARD_NAME)
export class AMS_CARD extends LitElement {
// private states
@state() private _subtitle;
//etc
@state() private _isLoading = true;
@provide({ context: hassContext })
@state()
private _hass?;
@provide({ context: deviceEntitesContext })
private _deviceEntities: any;
@provide({ context: infoBarContext })
private _infoBar: {
active: boolean;
title: string;
sensors: { humidity: any; temperature: any };
} = {
active: false,
title: "",
sensors: {
humidity: null,
temperature: null,
},
};
config: any;
set hass(hass) {
this._hass = hass;
this._states = hass.states;
this.fetchDevices();
}
private async fetchDevices() {
this._isLoading = true;
try {
await this.filterBambuDevices();
this._infoBar.sensors = this.returnInfoBarSensors();
} finally {
this._isLoading = false;
}
}
setConfig(config) {
if (!config.ams) {
throw new Error("You need to select an AMS");
}
this.config = config;
this._subtitle = config.subtitle === "" ? nothing : config.subtitle;
this._deviceEntities = config.entities;
this._deviceId = config.ams;
this._style = config.style;
this._infoBar = {
active: config.show_info_bar ? true : false,
title: config.subtitle === "" ? nothing : config.subtitle,
sensors: {
humidity: null,
temperature: null,
},
};
this._showInfoBar = config.show_info_bar ? true : false;
this._showType = config.show_type ? true : false;
this._customHumidity = config.custom_humidity === "" ? nothing : config.custom_humidity;
this._customTemperature =
config.custom_temperature === "" ? nothing : config.custom_temperature;
}
render() {
if (this._isLoading) return nothing;
return html` `;
}
private returnInfoBarSensors() {
const sensors: { humidity: any; temperature: any } = {
humidity: null,
temperature: null,
};
//do stuff
return sensors;
}
}
Это возвращает Vector-AMS-card как SO:
@consume({ context: deviceEntitesContext, subscribe: true })
private _entities;
@property() public showType;
static styles = styles;
render() {
return html`
${this._entities?.spools?.map(
(spool: { entity_id: string }) => html`
`
)}
`;
}
}
< /code>
, который, наконец, вызывает компонент Spool и Modal здесь < /p>
import { customElement, property, state } from "lit/decorators.js";
import { html, LitElement, nothing } from "lit";
import styles from "./spool.styles";
import "../dialog/dialog";
import { getContrastingTextColor } from "../../../utils/helpers";
import { hassContext } from "../../../utils/context";
import { consume } from "@lit/context";
@customElement("ha-bambulab-spool")
export class Spool extends LitElement {
@consume({ context: hassContext, subscribe: true })
private hass;
@property({ type: Boolean }) public show_type: boolean = false;
@property({ type: String }) public entity_id;
@state() private color;
@state() private name;
@state() private active;
@state() private remaining;
@state() private tag_uid;
@state() private state;
@state() private remainHeight: number = 95;
@state() private resizeObserver: ResizeObserver | null = null;
@state() private _dialogOpen: boolean = false;
static styles = styles;
connectedCallback() {
super.connectedCallback();
this.updateFromHass();
// Create a bound instance method to avoid creating new functions on each resize
this._handleResize = this._handleResize.bind(this);
// Start observing the parent element for size changes
this.resizeObserver = new ResizeObserver(this._handleResize);
const rootNode = this.getRootNode() as ShadowRoot;
const parent = this.parentElement || (rootNode instanceof ShadowRoot ? rootNode.host : null);
if (parent) {
this.resizeObserver.observe(parent);
}
}
private _handleResize() {
// Only update if the component is still connected to the DOM
if (this.isConnected) {
this.calculateHeights();
this.updateLayers();
}
}
disconnectedCallback() {
super.disconnectedCallback();
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
}
}
firstUpdated() {
this.updateLayers();
}
private _handleClick() {
this._dialogOpen = true;
}
private _closeDialog() {
this._dialogOpen = false;
}
render() {
console.log("spool component render");
return html`
${this.modal()}
class="string-roll-container"
style="${this.active ? "animation: wiggle 3s linear infinite" : nothing}"
>
${this.active ? html`` : nothing}
${this.hass.states[this.entity_id]?.attributes?.remain > 0
? html`
${this.hass.states[this.entity_id]?.attributes?.remain}%
`
: nothing}
${this.show_type
? html`
${this.hass.states[this.entity_id]?.attributes.name}
`
: nothing}
`;
}
modal() {
if (!this._dialogOpen) return nothing;
console.log("spool component modal rendered");
return html`
${this.hass.states[this.entity_id].attributes.friendly_name}
Filament Information
Filament
${this.hass.states[this.entity_id].attributes.name}
${this.hass.states[this.entity_id].attributes.color}
Color
Nozzle Temperature
Minimum
${this.hass.states[this.entity_id].attributes.nozzle_temp_min}
${this.hass.states[this.entity_id].attributes.nozzle_temp_max}
Maximum
Load
Unload
`;
}
updateLayers() {
// Query the #string-roll element inside this component's shadow DOM
const stringRoll = (this.renderRoot as ShadowRoot).getElementById("v-string-roll");
if (!stringRoll) return;
const stringWidth = 2; // matches .string-layer width in CSS
const rollWidth = stringRoll.offsetWidth; // container width
// Calculate how many lines fit
const numLayers = Math.floor(rollWidth / (stringWidth * 2)); // 2 = line width + gap
// Clear previous layers
const previousLayers = this.renderRoot.querySelectorAll(".v-string-layer");
previousLayers.forEach((layer) => layer.remove());
// Add new layers
for (let i = 0; i < numLayers; i++) {
const layer = document.createElement("div");
layer.classList.add("v-string-layer");
// Calculate left position = (index + 1) * (width*2) - width
const leftPosition = (i + 1) * (stringWidth * 2) - stringWidth;
layer.style.left = `${leftPosition}px`;
stringRoll.appendChild(layer);
}
}
isAllZeros(str) {
return /^0+$/.test(str);
}
calculateHeights() {
// Skip calculation if modal is open to prevent unwanted updates
if (this._dialogOpen) return;
const maxHeightPercentage = 95;
const minHeightPercentage = 12;
// If not a Bambu Spool or remaining is less than 0
if (
this.isAllZeros(this.hass.states[this.entity_id]?.attributes.tag_uid) ||
this.hass.states[this.entity_id]?.attributes?.remain < 0
) {
this.remainHeight = maxHeightPercentage;
} else {
// Get the container's height
const container = this.renderRoot.querySelector(
".string-roll-container"
) as HTMLElement | null;
const containerHeight = container?.offsetHeight || 0;
// Calculate heights in pixels
const maxHeightPx = containerHeight * (maxHeightPercentage / 100);
const minHeightPx = containerHeight * (minHeightPercentage / 100);
// Calculate remain height based on the remain percentage
const remainPercentage = Math.min(
Math.max(this.hass.states[this.entity_id]?.attributes?.remain, 0),
100
);
this.remainHeight = minHeightPx + (maxHeightPx - minHeightPx) * (remainPercentage / 100);
// Convert back to percentage of container
this.remainHeight = (this.remainHeight / containerHeight) * 100;
}
// Ensure remainHeight is always a number and doesn't exceed maxHeightPercentage
this.remainHeight = Math.min(
Number(this.remainHeight) || maxHeightPercentage,
maxHeightPercentage
);
this.requestUpdate();
}
// Add willUpdate lifecycle method to handle hass changes
willUpdate(changedProperties) {
if (changedProperties.has("hass")) {
// Skip the update if dialog is open to prevent unwanted re-renders
if (!this._dialogOpen) {
this.updateFromHass();
}
}
}
// New method to handle state updates
private updateFromHass() {
if (!this.hass || !this.entity_id) return;
console.log("updateFromHass");
const newActive =
this.hass.states[this.entity_id]?.attributes.active ||
this.hass.states[this.entity_id]?.attributes.in_use
? true
: false;
// Only update if the active state has changed
if (this.active !== newActive) {
this.active = newActive;
// Only recalculate heights if dialog is not open
if (!this._dialogOpen) {
this.calculateHeights();
}
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/794 ... -re-render
Вложенное компонент литлевого модального закрытия при повторном рендеринге ⇐ Javascript
Форум по Javascript
1739821712
Anonymous
У меня есть родительский компонент под названием AMS-Card , как ниже. Некоторый код удален, чтобы сохранить его коротким, но полная версия здесь < /p>
Визуально, собрать вместе, это выглядит так:
Когда вы нажимаете на катушку, он открывает модал с дополнительной информацией. Проблема, которая у меня возникает, заключается в том, что это запускает его для повторного рендеринга, что закрывает модал. Я предполагаю, что в этот момент он исходит от родительской AMS-карты, но я немного не уверен. Собственное право, и это никогда не закрывается из -за обновлений, поэтому я думаю, что, возможно, это как -то связано с повторением, и, возможно, некоторыми справочными столкновениями или что -то в этом роде. Я честно не уверен на этом этапе
[b]амс-card.ts[/b]
@customElement(AMS_CARD_NAME)
export class AMS_CARD extends LitElement {
// private states
@state() private _subtitle;
//etc
@state() private _isLoading = true;
@provide({ context: hassContext })
@state()
private _hass?;
@provide({ context: deviceEntitesContext })
private _deviceEntities: any;
@provide({ context: infoBarContext })
private _infoBar: {
active: boolean;
title: string;
sensors: { humidity: any; temperature: any };
} = {
active: false,
title: "",
sensors: {
humidity: null,
temperature: null,
},
};
config: any;
set hass(hass) {
this._hass = hass;
this._states = hass.states;
this.fetchDevices();
}
private async fetchDevices() {
this._isLoading = true;
try {
await this.filterBambuDevices();
this._infoBar.sensors = this.returnInfoBarSensors();
} finally {
this._isLoading = false;
}
}
setConfig(config) {
if (!config.ams) {
throw new Error("You need to select an AMS");
}
this.config = config;
this._subtitle = config.subtitle === "" ? nothing : config.subtitle;
this._deviceEntities = config.entities;
this._deviceId = config.ams;
this._style = config.style;
this._infoBar = {
active: config.show_info_bar ? true : false,
title: config.subtitle === "" ? nothing : config.subtitle,
sensors: {
humidity: null,
temperature: null,
},
};
this._showInfoBar = config.show_info_bar ? true : false;
this._showType = config.show_type ? true : false;
this._customHumidity = config.custom_humidity === "" ? nothing : config.custom_humidity;
this._customTemperature =
config.custom_temperature === "" ? nothing : config.custom_temperature;
}
render() {
if (this._isLoading) return nothing;
return html` `;
}
private returnInfoBarSensors() {
const sensors: { humidity: any; temperature: any } = {
humidity: null,
temperature: null,
};
//do stuff
return sensors;
}
}
Это возвращает Vector-AMS-card как SO:
@consume({ context: deviceEntitesContext, subscribe: true })
private _entities;
@property() public showType;
static styles = styles;
render() {
return html`
${this._entities?.spools?.map(
(spool: { entity_id: string }) => html`
`
)}
`;
}
}
< /code>
, который, наконец, вызывает компонент Spool и Modal здесь < /p>
import { customElement, property, state } from "lit/decorators.js";
import { html, LitElement, nothing } from "lit";
import styles from "./spool.styles";
import "../dialog/dialog";
import { getContrastingTextColor } from "../../../utils/helpers";
import { hassContext } from "../../../utils/context";
import { consume } from "@lit/context";
@customElement("ha-bambulab-spool")
export class Spool extends LitElement {
@consume({ context: hassContext, subscribe: true })
private hass;
@property({ type: Boolean }) public show_type: boolean = false;
@property({ type: String }) public entity_id;
@state() private color;
@state() private name;
@state() private active;
@state() private remaining;
@state() private tag_uid;
@state() private state;
@state() private remainHeight: number = 95;
@state() private resizeObserver: ResizeObserver | null = null;
@state() private _dialogOpen: boolean = false;
static styles = styles;
connectedCallback() {
super.connectedCallback();
this.updateFromHass();
// Create a bound instance method to avoid creating new functions on each resize
this._handleResize = this._handleResize.bind(this);
// Start observing the parent element for size changes
this.resizeObserver = new ResizeObserver(this._handleResize);
const rootNode = this.getRootNode() as ShadowRoot;
const parent = this.parentElement || (rootNode instanceof ShadowRoot ? rootNode.host : null);
if (parent) {
this.resizeObserver.observe(parent);
}
}
private _handleResize() {
// Only update if the component is still connected to the DOM
if (this.isConnected) {
this.calculateHeights();
this.updateLayers();
}
}
disconnectedCallback() {
super.disconnectedCallback();
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
}
}
firstUpdated() {
this.updateLayers();
}
private _handleClick() {
this._dialogOpen = true;
}
private _closeDialog() {
this._dialogOpen = false;
}
render() {
console.log("spool component render");
return html`
${this.modal()}
class="string-roll-container"
style="${this.active ? "animation: wiggle 3s linear infinite" : nothing}"
>
${this.active ? html`` : nothing}
${this.hass.states[this.entity_id]?.attributes?.remain > 0
? html`
${this.hass.states[this.entity_id]?.attributes?.remain}%
`
: nothing}
${this.show_type
? html`
${this.hass.states[this.entity_id]?.attributes.name}
`
: nothing}
`;
}
modal() {
if (!this._dialogOpen) return nothing;
console.log("spool component modal rendered");
return html`
${this.hass.states[this.entity_id].attributes.friendly_name}
Filament Information
Filament
${this.hass.states[this.entity_id].attributes.name}
${this.hass.states[this.entity_id].attributes.color}
Color
Nozzle Temperature
Minimum
${this.hass.states[this.entity_id].attributes.nozzle_temp_min}
${this.hass.states[this.entity_id].attributes.nozzle_temp_max}
Maximum
Load
Unload
`;
}
updateLayers() {
// Query the #string-roll element inside this component's shadow DOM
const stringRoll = (this.renderRoot as ShadowRoot).getElementById("v-string-roll");
if (!stringRoll) return;
const stringWidth = 2; // matches .string-layer width in CSS
const rollWidth = stringRoll.offsetWidth; // container width
// Calculate how many lines fit
const numLayers = Math.floor(rollWidth / (stringWidth * 2)); // 2 = line width + gap
// Clear previous layers
const previousLayers = this.renderRoot.querySelectorAll(".v-string-layer");
previousLayers.forEach((layer) => layer.remove());
// Add new layers
for (let i = 0; i < numLayers; i++) {
const layer = document.createElement("div");
layer.classList.add("v-string-layer");
// Calculate left position = (index + 1) * (width*2) - width
const leftPosition = (i + 1) * (stringWidth * 2) - stringWidth;
layer.style.left = `${leftPosition}px`;
stringRoll.appendChild(layer);
}
}
isAllZeros(str) {
return /^0+$/.test(str);
}
calculateHeights() {
// Skip calculation if modal is open to prevent unwanted updates
if (this._dialogOpen) return;
const maxHeightPercentage = 95;
const minHeightPercentage = 12;
// If not a Bambu Spool or remaining is less than 0
if (
this.isAllZeros(this.hass.states[this.entity_id]?.attributes.tag_uid) ||
this.hass.states[this.entity_id]?.attributes?.remain < 0
) {
this.remainHeight = maxHeightPercentage;
} else {
// Get the container's height
const container = this.renderRoot.querySelector(
".string-roll-container"
) as HTMLElement | null;
const containerHeight = container?.offsetHeight || 0;
// Calculate heights in pixels
const maxHeightPx = containerHeight * (maxHeightPercentage / 100);
const minHeightPx = containerHeight * (minHeightPercentage / 100);
// Calculate remain height based on the remain percentage
const remainPercentage = Math.min(
Math.max(this.hass.states[this.entity_id]?.attributes?.remain, 0),
100
);
this.remainHeight = minHeightPx + (maxHeightPx - minHeightPx) * (remainPercentage / 100);
// Convert back to percentage of container
this.remainHeight = (this.remainHeight / containerHeight) * 100;
}
// Ensure remainHeight is always a number and doesn't exceed maxHeightPercentage
this.remainHeight = Math.min(
Number(this.remainHeight) || maxHeightPercentage,
maxHeightPercentage
);
this.requestUpdate();
}
// Add willUpdate lifecycle method to handle hass changes
willUpdate(changedProperties) {
if (changedProperties.has("hass")) {
// Skip the update if dialog is open to prevent unwanted re-renders
if (!this._dialogOpen) {
this.updateFromHass();
}
}
}
// New method to handle state updates
private updateFromHass() {
if (!this.hass || !this.entity_id) return;
console.log("updateFromHass");
const newActive =
this.hass.states[this.entity_id]?.attributes.active ||
this.hass.states[this.entity_id]?.attributes.in_use
? true
: false;
// Only update if the active state has changed
if (this.active !== newActive) {
this.active = newActive;
// Only recalculate heights if dialog is not open
if (!this._dialogOpen) {
this.calculateHeights();
}
}
}
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79446457/nested-litelement-component-modal-closing-on-re-render[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия