NXとWebコンポーネントを使用したフロントエンドフレームワーク間の共有コンポーネント
13930 ワード
これは3部ガイドの2番目です. Part 1 - Project Setup and Introduction to Web Component
Part 2 - Add Custom Style and Property Binding Part 3 - Output Event and Allow Retrocompatibility.
パート2 -カスタムスタイルとプロパティのバインドを追加
パート1では、Webコンポーネント、それらが何であるか、どのように使用するかを紹介しました.また、共有のWebコンポーネントライブラリと一緒に角度と反応プロジェクトをNXのワークスペースを作成し、設定します.属性に“listens”するカスタム要素を作成しました
1 . Webコンポーネントのスタイル
私たちのWebコンポーネントが動作しますが、彼らは現在、非常にフラットです.それにいくつかのCSSを追加するのは素晴らしいことだ.CSSクラスをタイトルに追加し、グローバルなスタイルシートをいくつかのルールで作成し、それが動作します.しかし、Webコンポーネントの背後にあるアイデアは、自動で十分な独立した要素を作成し、“外”から何かを必要としません.グローバルCSSクラスの追加は、ページ上のすべての要素に影響を与え、すぐにルールに満ちた巨大なファイルになります.
うまくいけば、Webコンポーネントを使用することができますShadow DOM , これにより、マークアップ構造、スタイル、および動作を隠して、ページ上の他のコードから分離することができます.おかげで、要素は衝突しません、そして、コードは単純できれいに保たれます.
私たちの図書館(パート1にセットアップ)に戻って、我々のタイトル構成要素を編集するか、新しいタイトル要素を作成しましょう.
もしまだ私たちが働いていることがあるなら、このように見えます(ログを削除しました).
まあ、そこにあることが判明template 我々のニーズに合う要素!テンプレートはDOMではレンダリングされませんが、JavaScript経由で参照することもできます.私たちは、私たちの影DOMの中にこのテンプレートを一度クローン化して、それを我々の構成要素の向こう側にアクセスできます.
私たちの構成要素クラスの外で1つをつくって、我々が好きであるスタイルを割り当てましょう.
今すぐ私たちのコンポーネント
さて、これはずっと良いです.私たちのテンプレートは、1回クローンされ
ときに属性を変更すると、我々は現在、テンプレート内の興味を更新することができます.我々が気にかけるだけで
すごい!私たちのスタイルは、コンポーネントにのみ適用されます!
2 . Webコンポーネントへのオブジェクトのパス
我々がしたい1つの一般的なことは、単純なストリングだけでなく、我々のウェブ構成要素にオブジェクトを渡すことです.
Webコンポーネントから
まずこれを試してみましょう
属性を通しての2.1の通過オブジェクト
当社のライブラリでは、カスタム属性を
内部
それは私たちが望むものではない.オブジェクト全体を受け取るには、
角度を更新
反応のために、それは非常に類似しています.オープン
オブジェクトを属性として渡す場合は、小さなオブジェクトを割り当てたい場合に十分です.しかし、理想的には、私たちは、属性を汚染したくないです、そして、最悪の場合、文字列化されて、あらゆる変化でパースされる必要があるオブジェクトを割り当てます.代わりにプロパティとして渡すことについてはどうですか?まあ、我々はすることができますが、それは少し難しいです.
2.2プロパティ経由でオブジェクトを渡す
Webコンポーネントにプロパティを受信できるようにするには、まずいくつかのコードを変更する必要があります.Webコンポーネントのライフサイクル機能
変更中ですが、その人の名前を表示し、ログだけではないようにしましょう.そうするために、我々のコンポーネントでテンプレートをつくってください.
これを修正して動作させるために、我々は今までしたことを忘れる必要があり、変更を検出する方法を実装する必要があります.あなた自身でそれを修正しようとするだけで解決策をスクロールすることができます.
私たちが選んだクラスのプロパティを必要とする
これを角度で試してみましょうが、この新しい要素をライブラリのエクスポートに追加することを忘れないでください!
コンポーネントに入力を渡すとき、角度はそれをその要素の特性として割り当てます.私たちは多くを変更する必要はありません.
あなたの角度のアプリでは、この行を追加します.
物事が少し異なっているところに反応しよう.JSX属性としてパスをプロップ
あなたの反応アプリでは、我々のコンポーネントへの参照を追加し、初期化の後、
素晴らしい、我々はほとんどすべての我々はしたいと思ったが、まだいくつかのことを追加する必要があります.
最後の部分では、Webコンポーネントでディスパッチされるカスタムイベントを追加し、古いブラウザでコンポーネントを使用するPolyfillを追加します.
ここでREPO全体を見つけることができます.
typoまたはいくつかの問題を発見?
あなたがtypo、改善されることができた文またはこのブログ柱で更新されるべき何かを見つけたならば、あなたはそれをGITリポジトリでアクセスして、プル要求をすることができます.直接あなたの変更を使用して新しいプル要求を開きます.
Part 2 - Add Custom Style and Property Binding
パート2 -カスタムスタイルとプロパティのバインドを追加
パート1では、Webコンポーネント、それらが何であるか、どのように使用するかを紹介しました.また、共有のWebコンポーネントライブラリと一緒に角度と反応プロジェクトをNXのワークスペースを作成し、設定します.属性に“listens”するカスタム要素を作成しました
title
と変更DOM
したがって内容.この2番目の部分では、スタイリングによってさらに進んで、Webコンポーネントにプロパティを割り当てます.1 . Webコンポーネントのスタイル
私たちのWebコンポーネントが動作しますが、彼らは現在、非常にフラットです.それにいくつかのCSSを追加するのは素晴らしいことだ.CSSクラスをタイトルに追加し、グローバルなスタイルシートをいくつかのルールで作成し、それが動作します.しかし、Webコンポーネントの背後にあるアイデアは、自動で十分な独立した要素を作成し、“外”から何かを必要としません.グローバルCSSクラスの追加は、ページ上のすべての要素に影響を与え、すぐにルールに満ちた巨大なファイルになります.
うまくいけば、Webコンポーネントを使用することができますShadow DOM , これにより、マークアップ構造、スタイル、および動作を隠して、ページ上の他のコードから分離することができます.おかげで、要素は衝突しません、そして、コードは単純できれいに保たれます.
私たちの図書館(パート1にセットアップ)に戻って、我々のタイトル構成要素を編集するか、新しいタイトル要素を作成しましょう.
もしまだ私たちが働いていることがあるなら、このように見えます(ログを削除しました).
export class DemoTitleElement extends HTMLElement {
public static observedAttributes = ['title'];
attributeChangedCallback(name: string, old: string, value: string) {
this.innerHTML = `<h1>Welcome From ${this.title}!</h1>`;
}
}
customElements.define('demo-title', DemoTitleElement);
上の例では、属性のすべての更新に対して、要素内のHTML全体を置き換えただけですtitle
: attributeChangedCallback(name: string, old: string, value: string) {
this.innerHTML = `<h1>Welcome From ${this.title}!</h1>`;
}
ご存知かもしれませんが、この方法では<style>
タグ.したがって、手動でクラスを書き、いくつかのグローバルルールを追加しない限り、新しいスタイルを作成できません.また、我々の特質のあらゆる変化のためにすべての内容を再現するので、このアプローチはパフォーマンスでかなり貧弱です.いくつかの種類のテンプレートを持つことは素晴らしいです.まあ、そこにあることが判明template 我々のニーズに合う要素!テンプレートはDOMではレンダリングされませんが、JavaScript経由で参照することもできます.私たちは、私たちの影DOMの中にこのテンプレートを一度クローン化して、それを我々の構成要素の向こう側にアクセスできます.
私たちの構成要素クラスの外で1つをつくって、我々が好きであるスタイルを割り当てましょう.
const template = document.createElement('template');
template.innerHTML = `<style>
h1 {
color: red;
}
</style>
<h1>Welcome From <span id="title"></span>!</h1>`;
あなたは私がspan
とid
ここでは、動的なタイトルを書いてみたい.それは完全にテンプレートを再作成することなく私たちのDOMを更新するために便利になります.今すぐ私たちのコンポーネント
constructor
, シャドウDOMを付け、テンプレートを追加します.export class DemoTitleColoredElement extends HTMLElement {
public static observedAttributes = ['title'];
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
}
ここに我々が追加されますsuper
はconstructor
of HTMLElement
attachShadow
シャドウDOMツリーを指定した要素にアタッチし、そのシャドウルートへの参照を返します.つの異なることができますencapsulation modes
. open
シャドウルートの要素は、ルートの外側のJavaScriptからアクセス可能ですclosed
JavaScript外部からのクローズドシャドウルートのノードへのアクセスを拒否するthis.shadowRoot.appendChild
私たちはシャドウルートにテンプレートを加えて、使用していますtemplate.content.cloneNode(true)
テンプレートで定義したDOM要素をすべてクローニングします.shadowRoot
コンポーネントの内部.ときに属性を変更すると、我々は現在、テンプレート内の興味を更新することができます.我々が気にかけるだけで
title
属性は、単に以下を追加することができます. attributeChangedCallback() {
this.shadowRoot.getElementById('title').innerHTML = this.title;
}
以下のようなコンポーネントで終わります.const template = document.createElement('template');
template.innerHTML = `<style>
h1 {
color: red;
}
</style>
<h1>Welcome From <span id="title"></span>!</h1>`;
export class DemoTitleColoredElement extends HTMLElement {
public static observedAttributes = ['title'];
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
attributeChangedCallback() {
this.shadowRoot.getElementById('title').innerHTML = this.title;
}
}
customElements.define('demo-title-colored', DemoTitleColoredElement);
いいねこの新しい要素をライブラリのエクスポートに追加し、角度および/または反応プロジェクトでそれを試してください.私は以前に作成した古いタイトルと下部に新しいものを追加しました.私たちが見ることを期待するのは、我々の新しいタイトルが赤でなければならなくて、影響を及ぼさない間、黒人のままでいる我々の古いタイトルですh1
その外に<demo-title [title]="'Angular'"></demo-title>
<demo-title-colored [title]="'Angular'"></demo-title-colored>
すごい!私たちのスタイルは、コンポーネントにのみ適用されます!
2 . Webコンポーネントへのオブジェクトのパス
我々がしたい1つの一般的なことは、単純なストリングだけでなく、我々のウェブ構成要素にオブジェクトを渡すことです.
Webコンポーネントから
attributeChangedCallback
and observedAttributes
排他的な作品attribute
, 可能な解決策は、オブジェクトをstringifyすることになっていて、属性 data-*
HTMLは有効なままです.まずこれを試してみましょう
object
当社のWebコンポーネントには、単にその人の名前を表示します.属性を通しての2.1の通過オブジェクト
当社のライブラリでは、カスタム属性を
data-person
export class DemoObjectElement extends HTMLElement {
public static observedAttributes = ['data-person'];
attributeChangedCallback(name: string, old: string, value: string) {
console.log(`Attribute ${name} value:`, value);
}
}
customElements.define('demo-object', DemoObjectElement);
この新しい要素をライブラリのエクスポートに追加し、角で最初に使いましょう.内部
app.component.ts
このオブジェクトを作成します person = {
firstName: 'Jack',
lastName: 'Doe'
}
移動するapp.component.html
Webコンポーネントにオブジェクトを追加して割り当てます.使用する角度を指定する必要がありますattribute
との結合をprefixすることでattr.person
<demo-object [attr.data-person]="person"></demo-object>
Webコンポーネントログは次のようになります.Attribute data-person value: [object Object]
それは私たちが望むものではない.オブジェクト全体を受け取るには、
JSON.stringify
最初にJSON.parse
...角度を更新
app.component.ts
次の行を指定します.JSON = JSON;
テンプレート<demo-object [data-person]="JSON.stringify(person)"></demo-object>
また、Webコンポーネントログを更新することもできますobject
console.log(`Attribute ${name} value:`, JSON.parse(value));
期待通りにオブジェクトを表示します.Attribute data-person value: {"firstName":"Jack","lastName":"Doe"}
反応のために、それは非常に類似しています.オープン
app.tsx
次のように追加します. return (
<div className={styles.app}>
<demo-object dataPerson={JSON.stringify(person)}/>/>
</div>
);
私たちのWebコンポーネントは、“ネイティブ”のHTML要素は、単に割り当てdataPerson
属性を変更するdata-person
.オブジェクトを属性として渡す場合は、小さなオブジェクトを割り当てたい場合に十分です.しかし、理想的には、私たちは、属性を汚染したくないです、そして、最悪の場合、文字列化されて、あらゆる変化でパースされる必要があるオブジェクトを割り当てます.代わりにプロパティとして渡すことについてはどうですか?まあ、我々はすることができますが、それは少し難しいです.
2.2プロパティ経由でオブジェクトを渡す
Webコンポーネントにプロパティを受信できるようにするには、まずいくつかのコードを変更する必要があります.Webコンポーネントのライフサイクル機能
attributes
そこで、手動でプロパティの変更を検出する必要があります.また、最後の部分で述べたように、Webコンポーネントは基本的なHTMLのように消費されます.HTML要素に渡すプロパティは、使用するフレームワークによって異なります.変更中ですが、その人の名前を表示し、ログだけではないようにしましょう.そうするために、我々のコンポーネントでテンプレートをつくってください.
const template = document.createElement('template');
template.innerHTML = `
<style>
.name {
font-weight: bold;
}
</style>
<p>
<span class="name">first name</span>
<span id="firstName"></span>
</p>
<p>
<span class="name">last name</span>
<span id="lastName"></span>
</p>`;
コンポーネントのコードを変更しましょう.という名前のプロパティを渡すperson
Webコンポーネントに.もし我々が今までやっているようなことをすれば、次のようになるだろう.export class DemoProfileElement extends HTMLElement {
public static observedAttributes = ['person'];
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
attributeChangedCallback() {
this.update(this.person);
}
update(person: {firstName: string, lastName: string}) {
this.shadowRoot.getElementById('firstName').innerHTML = person.firstName;
this.shadowRoot.getElementById('lastName').innerHTML = person.lastName;
}
}
customElements.define('demo-profile', DemoProfileElement);
しかし、あなたのタイプスクリプトが言うかもしれません.person
はHTML属性ではないので、this.person
が定義されていない.加えてattributeChangedCallback
はobservedAttributes = ['person'];
存在しない属性を監視できません.これを修正して動作させるために、我々は今までしたことを忘れる必要があり、変更を検出する方法を実装する必要があります.あなた自身でそれを修正しようとするだけで解決策をスクロールすることができます.
私たちが選んだクラスのプロパティを必要とする
_person
, とget/set
それはその財産に割り当てられるexport class DemoProfileElement extends HTMLElement {
_person = {
firstName: '',
lastName: '',
};
get person(){
return this._person
}
set person(value: {firstName: string, lastName: string}){
this._person = value;
this.update(this._person);
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
update(person: {firstName: string, lastName: string}) {
this.shadowRoot.getElementById('firstName').innerHTML = person.firstName;
this.shadowRoot.getElementById('lastName').innerHTML = person.lastName;
}
}
customElements.define('demo-profile', DemoProfileElement);
はい、プロパティを更新したときにget/setをチェックして、それに応じてDOMを更新します.これを角度で試してみましょうが、この新しい要素をライブラリのエクスポートに追加することを忘れないでください!
コンポーネントに入力を渡すとき、角度はそれをその要素の特性として割り当てます.私たちは多くを変更する必要はありません.
あなたの角度のアプリでは、この行を追加します.
<demo-profile [person]="person"></demo-profile>
そして、それ!物事が少し異なっているところに反応しよう.JSX属性としてパスをプロップ
<demo-profile person={person} />
は動作しません.WebコンポーネントをHTML要素として扱う必要があります.だから、参照を取得し、プロパティに割り当てます.あなたの反応アプリでは、我々のコンポーネントへの参照を追加し、初期化の後、
person
Webコンポーネントへのプロパティーimport { DemoCounterElement, DemoProfileElement } from '@demo-shared/demo-library';
export function App() {
const person = {
firstName: 'Jack',
lastName: 'Doe'
}
const profile = useRef<DemoProfileElement>(null);
useEffect(function () {
if(profile.current) {
profile.current.person = person
}
}, []);
return (
<div className={styles.app}>
<demo-profile ref={counter}/>
</div>
);
これはすべての反応のためですプロジェクトを起動し、同じ結果が表示されます!素晴らしい、我々はほとんどすべての我々はしたいと思ったが、まだいくつかのことを追加する必要があります.
最後の部分では、Webコンポーネントでディスパッチされるカスタムイベントを追加し、古いブラウザでコンポーネントを使用するPolyfillを追加します.
ここでREPO全体を見つけることができます.
typoまたはいくつかの問題を発見?
あなたがtypo、改善されることができた文またはこのブログ柱で更新されるべき何かを見つけたならば、あなたはそれをGITリポジトリでアクセスして、プル要求をすることができます.直接あなたの変更を使用して新しいプル要求を開きます.
Reference
この問題について(NXとWebコンポーネントを使用したフロントエンドフレームワーク間の共有コンポーネント), 我々は、より多くの情報をここで見つけました https://dev.to/crocsx/share-components-across-front-ends-frameworks-using-nx-and-web-components-part-2-4e5dテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol