#web #components #shadow-dom #lit-element
#веб #Компоненты #shadow-dom #освещенный элемент
Вопрос:
Я изменяю атрибут освещенного веб-компонента, но измененное значение не отображается.
У меня есть наблюдаемый массив: reports[], который будет заполнен в firstUpdated() URL-адресами отчетов, полученными из rest API. Загрузка массива выполняется:
this.reports.push({ "name" : report.Name, "url" : this.apiUrl "/" report.Name "?rs:embed=true" });
см. Ниже:
import { LitElement, html, css } from 'lit';
import {apiUrl, restApiUrl} from '../../config';
export default class Homepage extends LitElement {
static properties = {
apiUrl: '',
restApiUrl: '',
reports: []
}
...
constructor() {
super();
this.apiUrl = apiUrl;
this.restApiUrl= restApiUrl;
this.reports = [];
}
firstUpdated() {
...
// Fetch all reports from restApiUrl:
rsAPIDetails(restApiUrl).then(reports =>{
for(const report of reports.value)
{
rsAPIDetails(restApiUrl "(" report.Id ")/Policies").then(policies => {
for(const policy of policies.Policies)
{
if(policy.GroupUserName.endsWith(usernamePBI))
{
for(const role of policy.Roles)
{
if(role != null amp;amp; (role.Name== "Browser" || role.Name== "Content Manager"))
{
// User has access to this report so i'll push it to the list of reports that will show in the navbar:
this.reports.push({ "name" : report.Name, "url" : this.apiUrl "/" report.Name "?rs:embed=true" });
}
}
}
}
});
}
}).then(q => {
console.log(this.reports);
});
}
render() {
return html`
<div id="sidenav" class="sidenav">
...
<div class="menucateg">Dashboards</div>
${this.reports.map((report) =>
html`<a @click=${() => this.handleMenuItemClick(report.url)}>${report.name}</a>`
)}
<div class="menucateg">Options</div>
</div>
`;
}
На консоли я ясно вижу, что массив загружен с правильными значениями.
Но функция render () не обновит веб-компонент новыми значениями отчетов []:
Ссылки должны быть добавлены внутри div ‘Dashboards’
Если вместо этого я статически заполняю reports[] значениями (в ctor), ссылки отображаются просто отлично.
Так почему же компонент не обновляется при изменении наблюдаемого массива?
Спасибо!
Ответ №1:
Array.push изменяет массив, но не изменяет фактическое значение в памяти.
Чтобы LitElement отслеживал обновления массивов и объектов, обновление значения должно быть неизменяемым.
Например, мы можем заставить ваш пример работать, выполнив его таким образом:
const newReports = this.reports.slice();
newReports.push({ "name" : report.Name, "url" : this.apiUrl "/" report.Name "?rs:embed=true" });
this.reports = newReports;
Или с помощью распространения массива
this.reports = [...this.reports, { "name" : report.Name, "url" : this.apiUrl "/" report.Name "?rs:embed=true" }]
Причина, по которой это работает, заключается в том, что когда вы это делаете this.reports.push()
, вы на самом деле не меняете «ссылку» this.reports
, вы просто добавляете к ней объект. С другой стороны, когда вы переопределяете свойство с this.reports = ...
помощью, вы меняете «ссылку», поэтому LitElement знает, что значение изменилось, и это вызовет повторный рендеринг.
Это также верно для объектов. Допустим, у вас есть свойство obj
. Если вы обновили объект, просто добавив свойство, элемент не будет повторно отображаться.
this.obj.newProp = 'value';
Но если вы переопределите свойство объекта в целом, скопировав объект и добавив свойство, это приведет к правильному обновлению элемента.
this.obj = {...this.obj, newProp: 'value'}
Вы можете увидеть значения, которые отслеживаются и обновляются с помощью метода updated .
Комментарии:
1. Спасибо, это сработало! В конце концов я использовал синтаксис распространения.
2. Нет проблем! Я также добавлю, что на всякий случай, если вы делаете что-то, у чего нет обновления свойств, но вы хотите принудительно повторно отобразить компонент, вы всегда можете вызвать
this.requestUpdate()
.