Lit-elements,编写受控组件的惯用方式

tug*_*end 5 javascript web-component lit-element controlled-component

我正在通过@open-wc使用lit-elements,目前正在尝试编写一组嵌套的组件,其中内部组件是一个输入字段,并且某些祖先组件必须支持一些任意重写规则,例如“不允许输入数字” '。

我想弄清楚的是使用 lit-elements 构建它的正确方法什么。在React 中,我会使用一个“受控组件”,在这里可以轻松地强制所有组件提交给根组件属性。

下面的示例是我使用 Lit-Elements 提出的。有没有更好的方法来做到这一点

请注意; 因为我想忽略一些字符,所以挑战变得稍微困难​​一些。如果没有e.target.value = this.value;at 级别 5,输入 elmement 将与忽略字符上的组件状态不同。我希望整个组件链正确同步,因此以标头标签为例。

export class Level1 extends LitElement {
  static get properties() {
    return {
      value: { type: String }
    };
  }

  render() {
    return html`
      <div>
        <h1>${this.value}</h1>
        <level-2 value=${this.value} @input-changed=${this.onInput}></level-2>
      </div>`;
  }

  onInput(e) {
    this.value = e.detail.value.replace(/\d/g, ''); 
  }
}

...

export class Level4 extends LitElement {
  static get properties() {
    return {
      value: { type: String }
    };
  }

  render() {
    return html`
      <div>
        <h4>${this.value}</h4>
        <level-5 value=${this.value}></level-5>
      </div>`;
  }
}

export class Level5 extends LitElement {
  static get properties() {
    return {
      value: { type: String }
    };
  }

  render() {
    return html`
      <div>
        <h5>${this.value}</h5>
        <input .value=${this.value} @input=${this.onInput}></input>
      </div>`;
  }

  onInput(e) {
    let event = new CustomEvent('input-changed', {
      detail: { value: e.target.value },
      bubbles: true,
      composed: true
    });

    e.target.value = this.value;
    this.dispatchEvent(event);
  }
}

export class AppShell extends LitElement {
  constructor() {
    super();
    this.value = 'initial value';
  }

  render() {
    return html`
      <level-1 value=${this.value}></level-1>
    `;
  }
}
Run Code Online (Sandbox Code Playgroud)

稍后添加

一种方法是使用事件中的路径数组直接从根组件访问输入元素。

我认为这是一个更糟糕的解决方案,因为它会导致组件之间更强的耦合,即假设子组件是具有 value 属性的输入元素。

  onInput(e) {
    const target = e.path[0]; // origin input element
    this.value = e.path[0].value.replace(/\d/g, ''); 
    // controlling the child elements value to adhere to the colletive state
    target.value = this.value;
  }
Run Code Online (Sandbox Code Playgroud)

Ben*_*ers 1

不要编写你的事件,用你的逻辑在大父级中处理它们。让子级在事件中发送所有需要的信息,尽量不要依赖target父级的事件处理程序。

要接收更新,请让您的组件在共享 mixin 中订阅,这是 @mishu 的建议,它使用一些状态容器(在这里,我提出了一些虚构的状态解决方案)

import { subscribe } from 'some-state-solution';
export const FormMixin = superclass => class extends superclass {
  static get properties() { return { value: { type: String }; } } 
  connectedCallback() {
    super.connectedCallback();
    subscribe(this);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以在事件处理程序中处理任何特定于组件的副作用updated(仅限 UI - 在父级或状态容器中执行逻辑)

import { publish } from 'some-state-solution';
class Level1 extends LitElement {
  // ...
  onInput({ detail: { value } }) {
    publish('value', value.replace(/\d/g, '')); 
  } 
}
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用“event.composedPath()”方法来获取启动“&lt;input&gt;”。话虽如此,这将表单的模型与 DOM 紧密联系在一起,这是脆弱的。您可能会考虑使用有限状态机或其他类型的状态存储,例如代理甚至 Redux 存储。输入组件将仅触发与其数据相关的事件,父组件将捕获它们并更新模型或转换状态,并且更改将通过组件方法向下传播。如果我必须像这样将应用程序耦合到 DOM 结构,我宁愿在一个地方进行:父组件。 (2认同)