web-dev-qa-db-ja.com

Webコンポーネント、データの受け渡し

私の理解では、データはその属性を介してカスタムhtml要素に渡され、CustomEventをディスパッチすることで送信されます。

JavaScriptオブジェクトは、イベントのdetailフィールドで送信できますが、要素に大量のデータを渡す必要がある場合はどうでしょうか。 JavaScriptでオブジェクトを提供する方法はありますか。

たとえば、要素に、動的に初期化または変更する必要のある可変数のパーツが含まれている場合(可変行数のテーブルなど)コンポーネント内で解析されるJSON文字列で構成される属性を設定および変更することを想像できますが、続行するためのエレガントな方法のようには感じません。

<my-element tableRowProperties="[{p1:'v1', p2:'v2'}, {p1:'v1',p2:'v2'}, {p1:'v1',p2:'v2'}]"></my-element>

または、データのペイロードを含む外部からのイベントを要素にリッスンさせることができますか?

16
Johan Lundquist

データを渡す

本当に大量のデータをコンポーネントに渡したい/必要な場合は、4つの異なる方法で実行できます。

1)プロパティを使用します。これは、次のように要素に値を与えることでオブジェクトを渡すだけなので、最も簡単です:el.data = myObj;

2)属性を使用する個人的には、この方法でこの方法を使用するのは嫌いですが、一部のフレームワークでは、属性を介してデータを渡す必要があります。これは、質問で表示する方法に似ています。 <my-el data="[{a:1},{a:2}....]"></my-el>属性値のエスケープ に関連する規則に従うように注意してください。このメソッドを使用する場合、属性でJSON.parseを使用する必要があり、それは失敗する可能性があります。また、HTMLで非常にくなり、属性に大量のデータが表示される可能性があります。

3 子要素を通して渡す。<select>要素を<option>子要素と考えます。任意の要素タイプを子として使用でき、実際の要素である必要さえありません。 connectedCallback関数では、コードはすべての子を取得し、要素、その属性、またはそのコンテンツをコンポーネントが必要とするデータに変換するだけです。

4 se Fetch。要素のURLを指定して、独自のデータを取得します。 <img src="imageUrl.png"/>を考えてください。コンポーネントのデータがすでにある場合、これは適切なオプションではないように思われます。ただし、ブラウザーには、上記のオプション2と同様のデータ埋め込み機能がありますが、ブラウザーによって自動的に処理されます。

画像に埋め込まれたデータを使用する例を次に示します。

img {
  height: 32px;
  width: 32px;
}
<img src="data:image/svg+xml;charset=utf8,%3C?xml version='1.0' encoding='utf-8'?%3E%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 314.7 314.7'%3E%3Cstyle type='text/css'%3E .st0{fill:transparent;stroke:%23231F20;stroke-width:12;} .st1{fill:%23231F20;stroke:%23231F20;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:10;} %3C/style%3E%3Cg%3E%3Ccircle class='st0' cx='157.3' cy='157.3' r='150.4'/%3E%3Cpolygon class='st1' points='108,76.1 248.7,157.3 108,238.6'/%3E%3C/g%3E%3C/svg%3E">

また、Webコンポーネントで埋め込みデータを使用する例を次に示します。

function readSrc(el, url) {
    var fetchHeaders = new Headers({
      Accept: 'application/json'
    });

    var fetchOptions = {
      cache: 'default',
      headers: fetchHeaders,
      method: 'GET',
      mode: 'cors'
    };

    return fetch(url, fetchOptions).then(
      (resp) => {
        if (resp.ok) {
          return resp.json();
        }
        else {
          return {
            error: true,
            status: resp.status
          }
        }
      }
    ).catch(
      (err) => {
        console.error(err);
      }
    );
  }

  class MyEl extends HTMLElement {
    static get observedAttributes() {
      return ['src'];
    }

    attributeChangedCallback(attrName, oldVal, newVal) {
      if (oldVal !== newVal) {
        this.innerHtml = '';
        readSrc(this, newVal).then(
          data => {
            this.innerHTML = `<pre>
${JSON.stringify(data,0,2)}
            </pre>`;
          }
        );
      }
    }
  }

  // Define our web component
  customElements.define('my-el', MyEl);
<!--
This component would go load its own data from "data.json"
<my-el src="data.json"></my-el>
<hr/>
The next component uses embedded data but still calls fetch as if it were a URL.
-->
<my-el src="data:json,[{&quot;a&quot;:9},{&quot;a&quot;:8},{&quot;a&quot;:7}]"></my-el>

XHRを使用しても同じことができますが、ブラウザがWebコンポーネントをサポートしている場合は、おそらくフェッチをサポートしています。そして、本当に必要な場合、いくつかの優れたフェッチポリフィルがあります。

オプション4を使用する最大の利点は、URLからデータを取得するcanand youcanを直接埋め込むデータ。そして、これは、<img>のようなほとんどの事前定義されたHTML要素が正確に機能する方法です。

[〜#〜] update [〜#〜]

JSONデータをオブジェクトに取り込む5番目の方法を考えました。それは、コンポーネント内で<template>タグを使用することです。この場合、JSON.parseを呼び出す必要がありますが、JSONをそれほどエスケープする必要がないため、コードをクリーンアップできます。

class MyEl extends HTMLElement {
  connectedCallback() {
    var data;
    
    try {
      data = JSON.parse(this.children[0].content.textContent);
    }
    
    catch(ex) {
      console.error(ex);
    }

    this.innerHTML = '';
    var pre = document.createElement('pre');
    pre.textContent = JSON.stringify(data,0,2);
    this.appendChild(pre);
  }
}

// Define our web component
customElements.define('my-el', MyEl);
<my-el>
  <template>[{"a":1},{"b":"&lt;b>Hi!&lt;/b>"},{"c":"&lt;/template>"}]</template>
</my-el>

データを渡す

コンポーネントからデータを取得するには、次の3つの方法があります。

1)プロパティから値を読み取ります。プロパティは何でもよく、通常は希望するデータの形式になるため、これは理想的です。プロパティは、文字列、オブジェクト、数値などを返すことができます。

2)属性を読み取ります。これには、コンポーネントが属性を最新の状態に保つ必要があり、すべての属性が文字列であるため最適ではない場合があります。したがって、ユーザーは、値に対してJSON.parseを呼び出す必要があるかどうかを知る必要があります。

3)イベント。これはおそらく、コンポーネントに追加する最も重要なことです。コンポーネントの状態が変化すると、イベントがトリガーされます。イベントは、ユーザーの操作に基づいてトリガーする必要があり、何かが発生したか、何かが利用可能であることをユーザーに警告するだけです。従来は、イベントに関連データを含めていました。これにより、コンポーネントのユーザーが記述する必要があるコードの量が削減されます。はい、彼らはまだプロパティまたは属性を読み取ることができますが、イベントにすべての関連データが含まれている場合、おそらく追加の操作を行う必要はありません。

24
Intervalia

上記の@Intervaliaの答えに非常に似ていますが、<script>タグの代わりに<template>タグを使用する6番目の方法があります。

これは、 マークダウン要素 で使用されるのと同じアプローチです。

class MyEl extends HTMLElement {
  connectedCallback() {
    var data;
    
    try {
      data = JSON.parse(this.children[0].innerHTML);
    }
    
    catch(ex) {
      console.error(ex);
    }

    this.innerHTML = '';
    var pre = document.createElement('pre');
    pre.textContent = JSON.stringify(data,0,2);
    this.appendChild(pre);
  }
}

// Define our web component
customElements.define('my-el', MyEl);
<my-el>
  <script type="application/json">[{"a":1},{"b":"<b>Hi!</b>"},{"c":"</template>"}]</script>
</my-el>
3
logan