Вложенное компонент литлевого модального закрытия при повторном рендерингеJavascript

Форум по Javascript
Ответить
Anonymous
 Вложенное компонент литлевого модального закрытия при повторном рендеринге

Сообщение Anonymous »

У меня есть родительский компонент под названием 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
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»