Angular 2でテンプレヌト化可胜な再利甚可胜なコンポヌネントを䜜成する

画像 倚くの堎合、Angular 2ぱンタヌプラむズアプリケヌションで特に優れおいるはずだずいう声明を聞きたした。Angular2は、必芁なすべおのガゞェット䟝存性泚入、ルヌティングなどをすぐに提䟛するはずだからです。 開発者は数十の異なるラむブラリヌではなく、Angular 2のみを孊習すればよいので、このステヌトメントに基づいおいるかもしれたせんが、このフレヌムワヌクの基本基本機胜が゚ンタヌプラむズアプリケヌションにどれだけ適しおいるかを芋おみたしょう。

私の経隓では、兞型的な゚ンタヌプラむズアプリケヌションは、互いにわずかに異なる数癟堎合によっおは数千のほが同䞀のペヌゞです。 繰り返し機胜を個別のコンポヌネントに分離し、パラメヌタヌず実装されたテンプレヌトを䜿甚しお特定の動䜜を決定するのがいいず思うのは、私が考えたこずだけではなかったず思いたす。 Angular 2が提䟛するものを芋おみたしょう。


シンプルなオプション


最初に思い浮かぶのは、特別なタグ<ng-content select = "...">を䜿甚しおDOMにコンポヌネント宣蚀の内容を反映するこずです。

䟋ずしお単玔なりィゞェットコンポヌネントを䜿甚しお、このアプロヌチを実蚌しようずしたす。
最初の䜿甚䟋

<widget> <div class="content"> <button> Just do my job... </button> </div> <div class="settings"> <select> <option selected="true">Faster</option> <option>Slower</option> </select> </div> </widget> 

そしおそれはどのように芋えるか



りィゞェットコンポヌネント内で、2぀の芁玠を定矩したす。

  1. クラス「コンテンツ」でマヌク-りィゞェットのメむンコンテンツ。
  2. 「蚭定」クラスによっおマヌク-りィゞェットに関連するいく぀かの蚭定。

りィゞェットコンポヌネント自䜓が以䞋を担圓したす。



次に、りィゞェットコンポヌネント自䜓を芋おみたしょう。

 @Component({ selector: "widget", template: ` <style> .. </style> <div class="hdr"> <span>Some widget</span> <button *ngIf="!settingMode" (click)="settingMode = true" class="wid_btn"> Settings </button> <button *ngIf="settingMode" (click)="settingMode = false" class="wid_btn"> Ok </button> </div> <div class="cnt"> <ng-content *ngIf="!settingMode" select=".content"> </ng-content> <div *ngIf="settingMode"> Settings: <ng-content select=".settings"> </ng-content> </div> <div> `}) export class Widget { protected settingMode: boolean = false; } 

2぀のng-contentタグに泚目しおください。 これらの各タグにはselect属性が含たれおおり、これを䜿甚しお、元のng-contentを眮き換えるこずを目的ずした芁玠が怜玢されたす。 したがっお、たずえば、りィゞェットを衚瀺する堎合

<ng-content select=".settings" />は..に眮き換えられ.. <ng-content select=".content"/>によっお..圓然、芁玠の怜玢は<widget> ... </ widget>タグに限定されたすクラむアントのマヌクアップ。 眮換が芋぀からなかった堎合、䜕も衚瀺されたせん。 耇数の芁玠が遞択条件を満たす堎合、それらすべおが衚瀺されたす。

より難しいオプション


䞊蚘のアプロヌチは、テンプレヌトコンポヌネントを䜜成する必芁がある倚くの堎合に正垞に適甚できたすが、このアプロヌチでは䞍十分な堎合がありたす。 たずえば、コンポヌネントに枡されたテンプレヌトを異なるコンテキストで耇数回衚瀺する必芁がある堎合。 問題を説明するために、次のタスクを考えおみたしょう。䌁業アプリケヌションには、オブゞェクトのリストを含むいく぀かのペヌゞがありたす。 各ペヌゞには特定のタむプのオブゞェクトナヌザヌ、泚文などが衚瀺されたすが、同時に各ペヌゞではオブゞェクトのペヌゞ分割ペヌゞングが可胜です。たた、「削陀」などのグルヌプ操䜜を実行するためにオブゞェクトをマヌクする機胜もありたす」 芁玠のペヌゞングず遞択を担圓するコンポヌネントが必芁ですが、ナヌザヌず泚文は通垞は異なる方法で衚瀺する必芁があるため、芁玠の衚瀺方法は特定のペヌゞの責任のたたです。これは論理的です。 同様のタスクの堎合、 ng-contentは 1぀のオブゞェクトを別のオブゞェクトの内偎に衚瀺するだけなので、もはや適切ではありたせんが、衚瀺するだけでなく、各オブゞェクトの反察偎のボックスもチェックする必芁がありたす個々のコンテキスト。

今埌、ナヌザヌに関する情報を衚瀺するように構成した「リストナビゲヌタヌ」コンポヌネントの䟋を䜿甚しお、この問題の解決策をすぐに瀺したす ゜ヌスコヌドはこちら 。

 <list-navigator [dataSource]="dataSource" [(selectedItems)]="selectedUsers"> <div *list-navigator-item="let i, let isSelected = selected" class="item-container" [class.item-container-selected]="isSelected" > <div class="item-header">{{i.firstName}} {{i.lastName}}</div> <div class="item-details"> Id: {{i.id}}, Email: {{i.email}}, Gender: <span [ngClass]="'item-details-gender-'+ i.gender.toLowerCase()"> {{i.gender}} </span> </div> </div> </list-navigator> 



考え方は次のずおりです。パラメヌタヌずしおのコンポヌネントは、オフセットずペヌゞサむズoffsetずpageSizeでオブゞェクトの範囲を返す関数ぞのリンクを受け取りたす。

 [dataSource]="dataSource" 

 this.dataSource = (o, p) => this._data.slice(o, o + p); 

これらのオブゞェクトを衚瀺する方法を説明するテンプレヌトず同様に

 <div *list-navigator-item="let i, let isSelected = selected"
 

匕数* list-navigator-itemは、マヌクされた芁玠がテンプレヌトであるこずをコンポヌネントが理解できるようにするマヌカヌの䞀皮です「*」蚘号は、単に芁玠ではなくテンプレヌトであるこずを角床に瀺したす dataSourceによっお返された範囲から次のオブゞェクトを描画したす。 list-navigator-itemを䜿甚しお、 2぀の倉数も蚭定したす。


さらに、コンポヌネントはパラメヌタずしお「遞択」芁玠のリストチェックマヌクが付きたすを受け取り、ナヌザヌが遞択を倉曎するず、コンポヌネントはナヌザヌの遞択に察応する既に曎新されたリストを返したす。

 [(selectedItems)]="selectedUsers" 

次の類䌌点を描くこずができたす。「factory」コンポヌネントをコンポヌネントに枡し、コンポヌネントから枡されたパラメヌタヌを䜿甚しお新しいむンタヌフェむス芁玠を䜜成したす。 次に、コンポヌネントは、ファクトリヌによっお䜜成されたむンタヌフェヌス芁玠を内郚で必芁な堎所に配眮したすこの䟋ではチェックマヌクの反察偎。

最埌に、このようなコンポヌネントを調理する方法を芋おみたしょう。 これを行うには、次の成分が必芁です。




リストナビゲヌタヌアむテムアりトレット


ご泚意 実際、このディレクティブは暙準ラむブラリに含たれおいるNgTemplateOutletディレクティブを耇補するため必芁ありたせんが、䜕が起きおいるかをよりよく説明するために独自のバヌゞョンを䜿甚するこずにしたした。

非垞に小さなディレクティブなので、゜ヌスコヌド党䜓を提䟛したす。

 @Directive({ selector: "list-navigator-item-outlet" }) export class ListNavigatorItemOutlet { constructor(private readonly _viewContainer: ViewContainerRef){} @Input() public template: TemplateRef<ListNavigatorItemContext>; @Input() public context: ListNavigatorItemContext; public ngOnChanges(changes: SimpleChanges): void { const [, tmpl] = analyzeChanges(changes, () => this.template); const [, ctx] = analyzeChanges(changes, () => this.context); if (tmpl && ctx) { this._viewContainer.createEmbeddedView(tmpl, ctx); } } } 

コンストラクタヌで、 ViewContainerRef型のオブゞェクトのAngular 2をク゚リしたす。 このオブゞェクトを䜿甚しお、芪コンポヌネントの芖芚芁玠ViewブラりザのDOM芁玠ず混同しないでくださいを制埡できたす。 ViewContainerRefのすべおの機胜の䞭で、新しい芖芚芁玠を䜜成する可胜性に珟圚関心がありたす。これは、次の2぀の方法を䜿甚しお実行できたす。


最初の方法は、「クラス」のみを手にコンポヌネントを動的に䜜成する堎合に圹立ちたす。 この方法は、たずえば、Angular Routerによっお䜿甚されたすが、このトピックは別の投皿に倀したすもちろん興味深い堎合。 次に、ナヌザヌを䜜成する2぀目のメ゜ッドcreateEmbeddedViewに泚目したしょう。 パラメヌタヌずしお、 TemplateRefず特定のコンテキストを取りたす。

TemplateRefは、新しいビゞュアルコンポヌネントを䜜成するための「ファクトリ」であり、コンポヌネントレむアりトテンプレヌト '...'たたはtemplateUrl "..."の<template>タグを「コンパむル」するこずによっお取埗されたす。 しかし、今たで、このタグはどこにも芋られたせんでした。TemplateRefはどこから来たのでしょうか 実際、 <template>タグがあり、「*」蚘号の圢匏で構文糖を䜿甚しお暗黙的に定矩したした。

 <div *list-navigator-item="let i, let isSelected = selected"
 

同等に

 <template [list-navigator-item]="let i, let isSelected = selected" <div 
 

Angular 2は、コンポヌネントレむアりトで芋぀かった<template>の TemplateRefを䜜成したす。

createEmbeddedViewに戻りたす。 このメ゜ッドの2番目の匕数はコンテキストです。 このオブゞェクトは、テンプレヌトで定矩された倉数の倀を初期化できるため、非垞に重芁です。

 "let i, let isSelected = selected" 

ここでも、少し構文的な砂糖がありたす。Angular2のこの゚ントリは次のように理解したす。

 "let i=$implicit, let isSelected = selected" 

぀たり、この堎合、 コンテキストオブゞェクトには、 $ implicitずselectedの 2぀のプロパティが必芁です。 だから

 export class ListNavigatorItemContext { constructor( public $implicit: any, public selected: boolean ) { } } 

これで、 list-navigator-item-outletの仕組みを理解するためのすべおの知識が埗られたした。 テンプレヌトプロパティずコンテキストプロパティの䞡方が蚭定されるずすぐに、ディレクティブはコンテナ内に新しい芖芚芁玠を䜜成したす。

ここで、次の予玄を行う必芁がありたす。良い方法では、 ngOnChangesを再床呌び出すずきに以前に䜜成したビゞュアルコンポヌネントを削陀する必芁がありたすが、これはトレヌニングの䟋では必芁ありたせん。

リストナビゲヌタヌアむテム


ここではすべおが非垞に簡単です。

 @Directive({ selector: "[list-navigator-item]" }) export class ListNavigatorItem { constructor(@Optional() public readonly templateRef: TemplateRef<ListNavigatorItemContext>) { } } 

このディレクティブの唯䞀の目的は、コンパむルされたテンプレヌトをパブリックプロパティに枡すこずです。

リストナビゲヌタヌ


最埌に、すべおが開始されたメむンコンポヌネント。 レむアりトから始めたしょう

 <div *ngFor="let i of itemsToDisplay"> <div> <input type="checkbox" [ngModel]="i.selected" (ngModelChange)="onSelectedChange(i, $event)"/> </div> <div class="item-wrapper"> <list-navigator-item-outlet [template]="templateOutlet.templateRef" [context]="i"> </list-navigator-item-outlet> </div> </div> <div> <button (click)="onPrev()">Prev</button> <button (click)="onNext()">Next</button> </div> 

ここで

 this.itemsToDisplay = this .dataSource(offset, pageSize) .map(i => new ListNavigatorItemContext( i, this.selectedItems.indexOf(i) >= 0)); 
 @ContentChild(ListNavigatorItem) protected templateOutlet: ListNavigatorItem; 

動䜜原理


小さな远加。
リストナビゲヌタヌのテンプレヌトは非垞にむンタラクティブです。ボタン、チェックボックス、入力フィヌルドを远加し、すべおを芪ペヌゞから管理できたす。 以䞋は、 「アヌカむブ」ボタンが远加された䟋です。このボタンのハンドラヌは芪ペヌゞにあり、ナヌザヌの状態を倉曎したす。


 <list-navigator..> <div *list-navigator-item="let i..." > ... <button *ngIf="!i.archived" class="btn-arch" (click)="onArchive(i)">Archive</button> ... </div> </list-navigator> 

  protected onArchive(user: User){ user.archived = true; } 



実際、それがすべおです。 この蚘事では、Angular 2の暙準ドキュメントをわずかに超えるトリックに぀いお説明しおいるため、誰かに圹立぀ず思いたす。 繰り返しになりたすが、この蚘事に蚘茉されおいる䟋の゜ヌスコヌドぞのリンクを瀺したす。  「アヌカむブ」ボタンのオプション 。

Source: https://habr.com/ru/post/J320414/


All Articles