Освещенный веб-компонент не обновляется при изменении атрибута

#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() .