web-dev-qa-db-ja.com

Webコンポーネント(ネイティブUI)間の通信方法は?

UIプロジェクトの1つとこのプロジェクトにネイティブWebコンポーネントを使用しようとしています。Polymerなど)のようなフレームワークやライブラリは使用していません。何かありますか。 angularjs/angularのように2つのWebコンポーネント間で通信するための最良の方法または他の方法(メッセージバスの概念など)。

現在、UI Webコンポーネントでは、データの公開と受信にdispatcheventを使用していますが、addeventlistenerを使用しています。たとえば、ChatFormとChatHistoryの2つのWebコンポーネントがあります。

// chatform webcomponent on submit text, publish chattext data 
this.dispatchEvent(new CustomEvent('chatText', {detail: chattext}));

// chathistory webcomponent, receive chattext data and append it to chat list
this.chatFormEle.addEventListener('chatText', (v) => {console.log(v.detail);});

この目的のために他の方法が機能することを教えてください。ネイティブUI Webコンポーネントと簡単に統合できるpostaljsなどの優れたライブラリ。

8
Sandeep

<div><audio>のような組み込みコンポーネントのようにWebコンポーネントを見ると、あなた自身の質問に答えることができます。コンポーネントは互いに通信しません。

コンポーネント同士が直接通信できるようにすると、コンポーネントがなく、コンポーネントAをコンポーネントBなしで使用することはできません。これは、コンポーネント同士が緊密に結び付いているためです。

代わりに、2つのコンポーネントを所有する親コード内に、コンポーネントAからeventsおよびcall functionsまたはset parametersを受信できるようにするコードを追加します=コンポーネントB、およびその逆。

組み込みコンポーネントを使用したこのルールには2つの例外があると述べました。

  1. <label>タグ:for属性を使用して別のコンポーネントのIDを取り込み、設定されて有効であれば、<label>をクリックすると、フォーカスを他のコンポーネントに渡します。

  2. <form>タグ:これは、フォームの投稿に必要なデータを収集するための子であるフォーム要素を探します。

しかし、これらの両方はまだ何にも縛られていません。 <label>focusイベントの受信者に通知され、IDが設定されて有効である場合、または最初のフォーム要素に子としてのみ渡されます。また、<form>要素は、子要素の存在や、子孫要素のすべてを通過してフォーム要素である要素を見つけ、valueプロパティを取得するだけのことは関係ありません。

ただし、原則として、ある兄弟コンポーネントが別の兄弟コンポーネントと直接通信することは避けてください。上記の2つの例における相互通信の方法は、おそらく唯一の例外です。

代わりに、親コードはイベントをリッスンし、関数を呼び出すか、プロパティを設定する必要があります。

はい、その機能を新しい親コンポーネントでラップできますが、大量の悲しみを保存し、スパゲッティコードを避けてください。

一般的なルールとして、兄弟要素が互いに対話することを決して許可しません。また、それらが親と対話できる唯一の方法はeventsを介することです。親は、属性、プロパティ、関数を介して直接子供と話すことができます。ただし、他のすべての条件では回避する必要があります。

9
Intervalia

他の両方の回答の+1、コンポーネントが疎結合されているため、イベントが最適です

カスタムイベントのdetailでは、何でも送信できることに注意してください。

イベントドリブン関数の実行:

だから私は(疑似コード)を使用します:

ソリティア/フリーセルゲームを定義する要素:

_-> game Element
  -> pile Element
    -> slot Element
      -> card element
  -> pile Element
    -> slot Element
      -> empty
_

(ユーザーがドラッグした)カードを別の山に移動する必要がある場合、

イベントを送信します(DOMをゲーム要素にバブリングします)

_    //triggered by .dragend Event
    card.say(___FINDSLOT___, {
                                id, 
                                reply: slot => card.move(slot)
                            });    
_

注:replyは関数ですdefinition

allゲーム要素で____FINDSLOT____イベントをリッスンするように指示された場所に積み重なるため...

_   pile.on(game, ___FINDSLOT___, evt => {
                                      let foundslot = pile.free(evt.detail.id);
                                      if (foundslot.length) evt.detail.reply(foundslot[0]);
                                    });

_

_evt.detail.id_に一致する1つのパイルのみが応答します。

!!! byexecuting関数card送信__evt.detail.reply_

そして技術的になります:関数はpileスコープで実行されます!

上記のコードは疑似コードです!

なぜ?!

複雑に見えるかもしれません。
重要な部分は、pile要素の.move()メソッドにcard要素が結合されていないであることです。

onlyカップリングはイベントの名前です:____FINDSLOT____ !!!

つまり、cardは常に制御されており、同じイベント(名前)を次の目的に使用できます。

  • カードはどこに行くことができますか?
  • 最高の場所はどこですか?
  • リバーpileのどのカードがフルハウスになりますか?
  • ...

私のE-lementsコードでは、pileは_evt.detail.id_にも結合されていません。

CustomEventsは関数のみを送信します



.say().on()は、dispatchEventaddEventListenerの(すべての要素に対する)私のカスタムメソッドです

これで、任意のカードゲームを作成するために使用できるE要素が手に入りました。

..今月後半にGitHubに掲載されます

スニークピーク:

ライブラリは必要ありません。独自に記述してください 'メッセージバス'

私のelement.on()メソッドはaddEventListener関数をラップする数行のコードなので、簡単に削除できます。

_    $Element_addEventListener(
        name,
        func,
        options = {}
    ) {
        let BigBrotherFunc = evt => {                     // wrap every Listener function
            if (evt.detail && evt.detail.reply) {
                el.warn(`can catch ALL replies '${evt.type}' here`, evt);
            }
            func(evt);
        }
        el.addEventListener(name, BigBrotherFunc, options);
        return [name, () => el.removeEventListener(name, BigBrotherFunc)];
    },
    on(
        //!! no parameter defintions, because function uses ...arguments
    ) {
        let args = [...arguments];                                  // get arguments array
        let target = el;                                            // default target is current element
        if (args[0] instanceof HTMLElement) target = args.shift();  // if first element is another element, take it out the args array
        args[0] = ___eventName(args[0]) || args[0];                 // proces eventNR
        $Element_ListenersArray.Push(target.$Element_addEventListener(...args));
    },
_

.say( )はワンライナーです:

_    say(
        eventNR,
        detail,             //todo some default something here ??
        options = {
            detail,
            bubbles: 1,    // event bubbles UP the DOM
            composed: 1,   // !!! required so Event bubbles through the shadowDOM boundaries
        }
    ) {
        el.dispatchEvent(new CustomEvent(___eventName(eventNR) || eventNR, options));
    },
_

_    Danny.say(
      __NATIVE_ELEMENTS_LOVERS__, 
      () => mail('[email protected]' , 'Re: early access to your E-lements GitHub')
    );
_

実施例

親コード(html/css)では、<chat-form>によって発行されたイベントをサブスクライブし、そのメソッドを実行してイベントデータを<chat-history>に送信する必要があります(以下の作業例ではadd

// WEB COMPONENT 1: chat-form
customElements.define('chat-form', class extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `Form<br><input id="msg" value="abc"/>
      <button id="btn">send</button>`

    btn.onclick = () => {
      // can: this.onsend() or not recommended: eval(this.getAttribute('onsend'))
      this.dispatchEvent(new CustomEvent('send',{detail: {message: msg.value} }))
      msg.value = '';
    }
  }
})

// WEB COMPONENT 2: chat-history
customElements.define('chat-history', class extends HTMLElement {
  add(msg) {
    let s = ""
    this.messages = (this.messages || []).concat(msg);
    for (let m of this.messages) s += `<li>${m}</li>`
    this.innerHTML = `<div><br>History<ul>${s}</ul></div>`
  }
})

// -----------------
// PARENT CODE
// which subscribe chat-form send event, 
// receive message and set it to chat-history
// -----------------

myChatForm.addEventListener('send', e => {
  myChatHistory.add(e.detail.message)
});
<chat-form id="myChatForm"></chat-form>
<chat-history id="myChatHistory"></chat-history>
1

カスタムイベントは、疎結合のカスタム要素を処理する場合に最適なソリューションです。

逆に、1つのカスタム要素が参照によって他のカスタム要素を知っている場合は、カスタムプロパティまたはメソッドを呼び出すことができます。

//in chatForm element
chatHistory.attachedForm = this
chatHistory.addMessage( message )
chatHistory.api.addMessage( message )

上記の最後の例では、apiプロパティを介して公開された専用オブジェクトを通じて通信が行われます。

カスタム要素のリンク方法に応じて、イベント(一方の方法)とメソッド(他方の方法)を組み合わせて使用​​することもできます。

最後に、メッセージが基本的ないくつかの状況では、HTML属性を介して(文字列)データを通信できます。

chatHistory.setAttributes( 'chat', 'active' )
chatHistory.dataset.username = `$(this.name)`
1
Supersharp