プロパティのプロパティ@


LitElement それはWebコンポーネントの標準に基づいているので、軽量なWebアプリケーションを構築するための優れた代替手段です.
本稿では、実用化について説明する@property and @internalProperty タイプスクリプト実装を使用したデコレータ

問題


ユーザーのリストを表示する小さなアプリケーションを構築しているとしましょう.あなたがそれらのいずれかを選択すると、他の詳細が表示される必要があります.この場合、アドレス情報である.

次に、LitElementを使用してWebコンポーネントを実装する前にデータモデルとデータセットを定義します.

データモデリングとデータセット


データモデル


モデルを準備するために、タイプスクリプトインターフェースと静的タイピングに頼りましょう.
// model.ts
export interface User {
  id: number;
  name: string;
}

export interface Address {
  country: string;
  state: string;
  city: string;
  street: string;
  zipCode: number;
}
注意してくださいAddress インターフェイスは、完全なアドレスを表す一連の属性を表示します.

データ源


テストを簡単にするには、前のモデルに一致するエンティティのデータセットを作成します.
// data.ts
import { Address } from "./model";
import { User } from "./model";

export const users: User[] = [
  {
    id: 0,
    name: "Donald Mayfield"
  },
  {
    id: 1,
    name: "Jill J. Fritz"
  },
  {
    id: 2,
    name: "Terry Buttram"
  }
];

export const address: Address[] = [
  {
    street: "2180 BELLFLOWER",
    country: "USA",
    state: "AL",
    city: "Madison",
    zipCode: 35064
  },
  {
    street: "845 ODOM ROAD, SUITE 200",
    country: "USA",
    state: "CA",
    city: "Los Angeles",
    zipCode: 90720
  },
  {
    street: "9025 QUEENS BLVD",
    country: "USA",
    state: "NY",
    city: "Queens",
    zipCode: 11355
  }
];
の間の関係users and address 簡単です:ユーザid の位置と一致するaddress 配列.

要素の使用


LitElementプロパティを管理するためのさまざまな方法を提供します.ドキュメントが言うように

LitElement manages your declared properties and their corresponding attributes.


これは静的などちらかを使用して行うことができますproperties フィールドまたはデコレータを使用します.飾り付けの力を使って、タイプスクリプトの方法を取りましょう.
次のセクションでは、「プロパティ管理」をよりよく理解します.

mainViewerコンテナの作成


最初のコンポーネントを含むクラスを作りましょう.この場合、ユーザーの一覧とその詳細を表示するコンテナコンポーネントを定義します.
import {
  LitElement,
  html,
  customElement,
  css,
  internalProperty
} from "lit-element";
import { users } from "./data";
import { User } from "./model";

@customElement("main-viewer")
class MainViewer extends LitElement {
  static styles = css`
    :host {
      display: block;
    }
  `;

  users: User[] = [];
  userId?: number;

  constructor() {
    super();
  }

  render() {
    return html`
      <div>
        <h1>User Address Viewer</h1>
        <span>Select a User to see the Address:</span>
        <ul>
          ${this.users.map(
            user => html`
              <li>
                <a href="#" @click="${() => this.viewAddress(user.id)}"
                  >${user.name}</a
                >
              </li>
            `
          )}
        </ul>
      </div>
    `;
  }

  async connectedCallback() {
    super.connectedCallback();
    this.users = await this.getUsers();
  }

  private getUsers() {
    // in the real-world you'll get the data from a service, file, etc.
    return new Promise<User[]>((resolve, reject) => resolve(users));
  }

  private viewAddress(id: number) {
    this.userId = id;
  }
}
これまでにこのコンポーネントで起こっていることを説明しましょう.
  • The static styles 属性は、タグ付けされたテンプレートリテラルを使用して、コンポーネントのスタイルを定義しますcss ).
  • つのクラスプロパティを定義します.users メインリストに表示されるユーザーの集合を「ストア」する.また、userId が選択されたユーザー識別子のリファレンスを含んでいる(この値は異なるユーザを選択する度に変更される).
  • The render メソッドは、テンプレートリテラルを通してHTML内容を返しますhtml ). この関数は、コンポーネントのプロパティが変更されるといつでも呼び出されます.
  • The connectedCallback 関数は呼び出しを行うgetUsers コンポーネントがドキュメントのDOMに追加されたときに初期データを取得します.
  • The getUsers 関数はデータを取得するために非同期呼び出しを実行する.
  • The viewAddress 関数はid ユーザーがリンクの上にクリック行動を実行すると選択されるオブジェクトの.次に、クラスプロパティuserId 更新されます.
  • 前のコードは完璧です.しかし、それはまだデータを表示されません!我々は、ページのタイトルと空白のセクションだけを表示するユーザーのリストが表示されます.

    @ internalPropertyデコレータを使用する


    このレンダリングの問題を解決するには、Webコンポーネントの更新サイクルをトリガーする必要があります.
    しばらくすると更新サイクルを実行する必要があります:あなたがリストを変更するたびにusers ! ただし、クラスから任意の変数や属性を更新するたびに行うことはできません.代わりに、どの属性が「操作」を実行するか、またはコンポーネントを更新しておくために、「キー」であることを言うべきです.LitElementプロパティをこれらのキー属性に呼び出します.
    これらを識別したら、これらの属性をプロパティとして宣言する必要があります.
    // main-viewer.ts
    
    @customElement("main-viewer")
    class MainViewer extends LitElement {
      // ...
    
      @internalProperty() users: User[] = [];
      @internalProperty() userId?: number;
    
      constructor() {
        super();
      }
    
      // ...
    }
    
    なぜ使用@internalProperty この場合
  • このMainViewer コンポーネントを参照する必要はありませんusers or userId コンポーネントの外部から.
  • 前の点からusers and userId 属性としてプライベートまたはプロテクト.
  • 言い換えれば、LitElementは、これらのプロパティの変更に対して「反応」し、テンプレートが自動的にレンダリング/更新されるようにします.

    @プロパティデコレータの使用


    AddressViewerコンポーネントの作成


    子コンポーネントを作成する前に、コンテナから使用する方法について考えてみましょう.Webコンポーネントでは、次の要素と属性が必要になるとしましょう.
    <address-viewer .userId=${userId}></address-viewer>
    
    それは新しいaddress-viewer コンポーネントは「受信」する必要がありますuserId それぞれのアドレスを取得できる値.The .userId=${userId} 表記は、データへの一方向のデータバインディングを適用しますAddressViewer コンポーネントにはuserId 公共財産として.

    プロパティVSデコレーター


    前の考慮を考慮に入れて、新しいファイルを作成しましょうaddress-viewer.ts :
    import {
      LitElement,
      html,
      property,
      customElement,
      css,
      internalProperty
    } from "lit-element";
    import { address } from "./data";
    import { Address } from "./model";
    
    @customElement("address-viewer")
    class AddressViewer extends LitElement {
      static styles = css`
        :host {
          display: block;
        }
    
        table {
          border-collapse: collapse;
          width: 100%;
        }
    
        td,
        th {
          border: 1px solid gray;
          text-align: left;
          padding: 5px;
        }
      `;
    
      @property({ type: Number }) userId: number;
      @internalProperty() userAddress?: Address;
    
      constructor() {
        super();
      }
    
      render() {
        if (this.userAddress === undefined) {
          return html``;
        }
    
        return html`
          <table>
            <tr>
              <th>Country</th>
              <th>State</th>
              <th>City</th>
              <th>Street</th>
              <th>Zip Code</th>
            </tr>
            <tr>
              <td>${this.userAddress.country}</td>
              <td>${this.userAddress.state}</td>
              <td>${this.userAddress.city}</td>
              <td>${this.userAddress.street}</td>
              <td>${this.userAddress.zipCode}</td>
            </tr>
          </table>
        `;
      }
    
      update(changedProperties: Map<string, unknown>) {
        if (changedProperties.has("userId")) {
          const oldValue = changedProperties.get("userId") as number;
          console.log("userId updated, newVal", this.userId, "oldVal", oldValue);
          this.loadAddress(this.userId);
        }
        super.update(changedProperties);
      }
    
      private async loadAddress(id: number) {
        this.userAddress = await this.getAddress(id);
      }
    
      private getAddress(id: number) {
        return new Promise<Address>((resolve, reject) => resolve(address[id]));
      }
    }
    
    もう一度、前のソースコードを詳しく見てみましょう.

    - UserID属性は、@ propertyデコレータを使用しているコンポーネントのパブリックプロパティとして定義されます。


    The userAddress プロパティは、コンポーネントのパブリックプロパティである必要はありません.代わりに、@internalProperty それが変更されると、更新サイクルをトリガするデコレータ.

    -最初は未定義です


    The render プロパティは、プロパティが変更されるたびにレンダリングされるHTMLコンテンツを返します.

    注意-この関数は、ユーザアドレスに必要なオブジェクトが含まれている場合にのみ、意味のあるテンプレートを返します。


    The update 関数はプロパティ値を反映し、render 関数.
  • 受信するMap 変更されたプロパティを指定します.
  • それはuserId プロパティが変更され、呼び出しを実行するloadAddress .
  • このメソッドをオーバーライドするたびにコールする必要がありますsuper.update() テンプレートをレンダリングするには
  • The loadAddress 関数は、userId そして、アドレスオブジェクトを取得するユーティリティ関数を呼び出します.
  • 真新しいAddressViewer コンポーネントは準備ができており、メインコンテナの子コンポーネントとして使用する必要があります.更新しましょうrender 以下のように機能します:
    // main-viewer.ts
    
    // Let's import the <address-viewer> definition
    import "./address-viewer";
    
    @customElement("main-viewer")
    class MainViewer extends LitElement {
      //...
    
      render() {
        return html`
          <div>
            <h1>User Address Viewer</h1>
            <span>Select a User to see the Address:</span>
            <ul>
              ${this.users.map(
                user => html`
                  <li>
                    <a href="#" @click="${() => this.viewAddress(user.id)}"
                      >${user.name}</a
                    >
                  </li>
                `
              )}
            </ul>
            <address-viewer .userId=${this.userId}></address-viewer>
          </div>
        `;
      }
    
     // ... 
    }
    
    一方、一方向データ結合に注意を払う.userId=${this.userId} これは<address-viewer> コンポーネントuserId 変更されます.これは魔法ですね.

    ライブデモ


    このコードを再生するには?StackBlitzエディタを開きます
    質問があれば遠慮なく手を差し伸べる.フォローミーオンGitHub 私の仕事についてもっと見る.
    このドットラボは、企業のデジタル変換の努力を実現支援に焦点を当てた現代のWebコンサルティングです.専門家の建築指導、訓練、またはコンサルティング、角度、Vue、Webコンポーネント、Graphql、ノード、バゼル、またはポリマー、訪問thisdotlabs.com .
    このドットメディアは、すべての包括的で教育的なウェブを作成することに集中します.我々は最新のイベント、ポッドキャスト、および無料のコンテンツを介して近代的なWebの進歩と最新の状態に保つ.学ぶthisdot.co .