LitElement保持内部状态

时间:2020-06-26 11:56:09

标签: javascript lit-element lit-html

我正在尝试使用LitElement构建图表。图表从用户那里获取数据属性,并显示此数据(图表图)。它还从数据中获取系列名称,以便显示带有每个系列复选框的图例,可用于在统计图中显示或隐藏该系列的数据。
下面是一个非常简单的示例,其中图表图只是包含数据点(3、5、4、7)的div,图例只是复选框。预期的行为是,当选择/取消选择复选框,在图表绘图对应的数据(数据的div)被示出/隐藏。例如,最初两个复选框都默认选中,并且两个系列的数据都正确显示。但是,如果取消选择第一个复选框,则我希望“ series1”的数据被隐藏,因此仅显示5和7。
这是复选框行为,我无法正常工作。当我选择或取消选择一个复选框时,我登录了this.series,它似乎已正确更新,反映了选中了哪个复选框,但是图表(数据div)未更新。

import { LitElement, css, html } from "lit-element";
import { render } from "lit-html";

class TestElement extends LitElement {
  static get properties() {
    return {
      data: { type: Array },
      series: { type: Array },
    };
  }

  constructor() {
    super();
    this.data = [];
    this.series = [];
  }
  checkboxChange(e) {
    const inputs = Array.from(this.shadowRoot.querySelectorAll("input")).map(n => n.checked);
    this.series = this.series.map((s, i) => ({ ...s, checked: inputs[i] }));
    console.log("this.series", this.series);
  }
  render() {
    this.series = Object.keys(this.data[0]).map(key => ({ key, checked: true }));
    const data = this.data.map(d => this.series.map(s => (s.checked ? html`<div>${d[s.key]}</div>` : "")));
    const series = this.series.map(
      s => html`<input type="checkbox" ?checked=${s.checked} @change=${this.checkboxChange} />`
    );
    return html`${data}${series}`;
  }
}
customElements.define("test-element", TestElement);

render(
  html`<test-element
    .data=${[
      { series1: "3", series2: "5" },
      { series1: "4", series2: "7" },
    ]}
  ></test-element>`,
  window.document.body
);

1 个答案:

答案 0 :(得分:1)

尝试以下操作:

import { LitElement, html } from 'lit-element';

class TestElement extends LitElement {
  static get properties() {
    return {
      data: { attribute: false, accessors: false },
      series: { attribute: false, accessors: false },
      checked: { attribute: false, accessors: false },
    };
  }

  constructor() {
    super();
    this.data = [];
    this.series = new Map();
    this.checked = new Map();
  }

  get data() {
    return this.__data || [];
  }

  set data(v) {
    const oldValue = this.__data;
    this.__data = Array.isArray(v) ? v : [];
    this.series = new Map();

    for (const row of this.data) {
      for (const [series, value] of Object.entries(row)) {
        this.series.set(series, [...this.series.get(series) || [], value])
      }
    }

    for (const series of this.series.keys()) {
      this.checked.set(series, this.checked.get(series) ?? true);
    }

    this.requestUpdate('data', oldValue);
    this.requestUpdate('series', null);
    this.requestUpdate('checked', null);
  }

  checkboxChange(e) {
    this.checked.set(e.target.dataset.series, e.target.checked);
    this.requestUpdate('checked', null);
  }

  render() {
    return [
      [...this.series.entries()].map(([series, values]) => values.map(value => html`
        <div ?hidden="${!this.checked.get(series)}">${value}</div>
      `)),
      [...this.checked.entries()].map(([series, checked]) => html`
        <input type="checkbox" ?checked=${checked} data-series="${series}" @change=${this.checkboxChange} />
      `)
    ];
  }
}

customElements.define("test-element", TestElement);

现场示例:https://webcomponents.dev/edit/FEbG9UA3nBMqtk9fwQrD/src/index.js

此解决方案提出了一些改进:

  1. 在数据更新时缓存系列和选中状态,而不是在每次渲染时缓存
  2. 使用 hidden attr 隐藏未选中的系列
  3. 使用数据属性将集合项的可序列化数据传递给事件侦听器。
  4. 使用 attribute: false 而不是 type: Array(假设您不需要从属性反序列化 data