9

问题:有没有办法在不触发其功能的情况下将属性更改传播到子元素render?目前,当我更新父级中的属性时,switchViewModeHandler它会触发对子级的重新渲染。

用例:将父级切换到“编辑”模式也应该为其所有子级切换相同。

疑问:我应该使用自定义事件吗?问题是它将是一个复杂的嵌套元素网络,其中的事件很快就会变得难以调试(Polymer 已经遇到过这个问题)。

设置:父元素:

class ParentElement extends LitElement {
  @property() viewMode;

  constructor() {
    this.viewMode = ViewMode.editable;
  }

  static get properties() {
    return {
      viewMode: { type: String, reflect: true },
    };
  }

  private switchViewModeHandler(event: MouseEvent): void {
    this.viewMode =
      (this.viewMode === ViewMode.editing) ? ViewMode.editable : ViewMode.editing; // update my own edit mode
    const options: HTMLElement[] = (Array.from(this.children) as HTMLElement[]);
    options.forEach((item: HTMLElement) => {
      item.viewMode = this.viewMode;
    });
  }

  render() {
    return html`
        <p>parent</p><label class="switch-wrapper">toggle edit mode
          <input type="checkbox"
            ?checked="${this.viewMode === ViewMode.editing}"
            @click="${
              (event: MouseEvent): void => this.switchViewModeHandler(event)
            }"
          />
        </label>
        <slot></slot><!-- child comes from here -->
    `;
  }
}

子元素:

class ChildElement extends LitElement {
  @property() viewMode;

  constructor() {
    super();
    this.viewMode = ViewMode.editable;
  }

  static get properties() {
    return {
      viewMode: { type: String, reflect: true },
    };
  }

  render() {
    console.log('child render() called');
    return html`
      <div class="viewer">child viewer</div>
      <div class="editor mode-${this.viewMode}">child editor</div>
    `;
  }
}

标记:

<parent-element>
  <child-element data-type="special"></child-element
></parent-element>

编辑模式来自一个导入的简单枚举(此处省略):

export enum ViewMode {
  readOnly = 'readOnly',
  editable = 'editable',
  editing = 'editing',
}

这是一个可以玩的 Codesandbox:https ://codesandbox.io/s/v1988qmn75

4

1 回答 1

7

将状态传播到轻 DOM 子项是一种相对很少需要的模式,因此我们还没有很好地记录它,但在某些情况下,像您这样的情况是必要的。

正确的方法实际上取决于容器元素的子元素的控制程度。如果您,父元素的作者,也是它的唯一用户,并且确切地知道子元素可以是什么,那么您就有更多的灵活性。

我能想到的选项是:

  • 无论类型如何,始终为孩子设置一个属性。这就像您<child prop=${x}></child>在 lit-html 中有可用的语法。
  • 在孩子身上触发一个事件。这是非常解耦的,但是如果您没有来自第三方的孩子,则成本更高,收益却很小。
  • 让孩子在家长那里注册。对孩子承担更多责任,但利用孩子的生命周期来订阅状态更改。

我想说您在事件处理程序中设置子状态的方法并不遥远,只是它与用户交互事件而不是状态本身过于相关。

我会选择你总是更新状态的东西updated()

class ParentElement extends LitElement {
  @property({ type: String, reflect: true })
  viewMode = ViewMode.editable;

  private _onSwitchClicked(event: MouseEvent): void {
    this.viewMode = (this.viewMode === ViewMode.editing)
      ? ViewMode.editable
      : ViewMode.editing;
  }

  private _onSlotChange() {
    this.requestUpdate();
  }

  updated() {
    for (const child of Array.from(this.children)) {
      child.viewMode = this.viewMode;
    }
  }

  render() {
    return html`
      <p>parent</p>
      <label class="switch-wrapper">toggle edit mode
        <input type="checkbox"
            ?checked=${this.viewMode === ViewMode.editing}
            @click=${this._onSwitchClicked}>
      </label>
      <slot @slotchange=${this._onSlotChange}></slot>
    `;
  }
}

如果子项是 LitElements 并且值是基元,则始终设置属性是可以的 - 它们将在子项中进行脏检查。

注意slotchange事件处理程序,<slot>以便我们可以观察孩子。

于 2019-03-17T23:16:54.590 回答