Halo semuanya.
Setiap orang yang menulis tentang kerangka Angular entah bagaimana menemukan (atau bahkan bekerja) dengan perpustakaan
Bahan Angular . Ini adalah pustaka komponen yang ditulis dengan sangat baik yang mampu menata gaya yang fleksibel, yang diimplementasikan melalui kemampuan untuk membuat berbagai tema untuk aplikasi Anda, dengan sejumlah besar komponen untuk semua kesempatan.
Dalam pekerjaan sehari-hari saya, tidak ada satu proyek pun yang dapat melakukannya tanpanya.
Tetapi di samping semua keuntungan dari fleksibilitas perpustakaan ini, juga dimungkinkan untuk memanfaatkan pengalaman para pembuat dalam menulis komponen mereka sendiri, dan bagi saya ini adalah panduan terbaik tentang pengembangan praktik terbaik di
Angular .
Pada artikel ini saya ingin berbagi dengan Anda bagaimana Anda dapat menerapkan pendekatan dengan templat kompleks yang diimplementasikan dalam modul
MatTableModule .
Sebagai contoh, saya ingin menunjukkan cara membuat
daftar kartu dengan kemampuan untuk menambahkan pagination dan filter , dan sebagai dasar kita mengambil model model komponen MatTable.
Templat (
sumber ):
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> <ng-container matColumnDef="position"> <th mat-header-cell *matHeaderCellDef> No. </th> <td mat-cell *matCellDef="let element"> {{element.position}} </td> </ng-container> <ng-container matColumnDef="name"> <th mat-header-cell *matHeaderCellDef> Name </th> <td mat-cell *matCellDef="let element"> {{element.name}} </td> </ng-container> <ng-container matColumnDef="weight"> <th mat-header-cell *matHeaderCellDef> Weight </th> <td mat-cell *matCellDef="let element"> {{element.weight}} </td> </ng-container> <ng-container matColumnDef="symbol"> <th mat-header-cell *matHeaderCellDef> Symbol </th> <td mat-cell *matCellDef="let element"> {{element.symbol}} </td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table>
Setelah mempelajari templat, menjadi jelas bahwa kami mengindikasikan dalam
wadah ng menandai markup untuk kolom tabel tertentu, tetapi bagaimana cara kerjanya di dalam? Ini adalah pertanyaan yang saya tanyakan ketika saya melihat desain ini, sebagian karena fakta bahwa saya tidak bekerja dengan komponen dinamis. Jadi, mari kita mulai
(kode sumber) .
Struktur
Seperangkat entitas yang perlu kita buat. Diagram blok ini menggambarkan interaksi mereka.

Langkah pertama
Kami membutuhkan layanan untuk mendaftarkan template mikro kami.
@Injectable() export class RegisterPropertyDef<T> {
Langkah kedua
Buat arahan untuk mendaftarkan template:
@Directive({ selector: '[providePropertyDefValue]' }) export class ProvidePropertyDefValueDirective<T> implements OnInit { @Input() providePropertyDefValueId: string; constructor( private container: ViewContainerRef, private template: TemplateRef<any>, // private registerPropertyDefService: RegisterPropertyDefService<any>, // @Optional() private parent: Alias<T[]> // ) {} ngOnInit(): void { this.container.clear();
Langkah ketiga
Buat komponen:
@Component({ selector: 'lib-card-list', template: ` <mat-card *ngFor="let source of sources"> <ul> <li *ngFor="let key of displayedColumns"> <span>{{ findColumnByKey(key)?.label }}</span> <span> <ng-container [ngTemplateOutlet]="findColumnByKey(key)?.template || default" [ngTemplateOutletContext]="{ $implicit: source }" ></ng-container> </span> </li> </ul> </mat-card> <ng-template #default></ng-template> `, styles: [ 'mat-card { margin: 10px; }' ] }) export class CardListComponent<T> implements OnInit, AfterViewInit { @Input() defaultColumns: DefaultColumn[]; @Input() source$: Observable<T[]>; displayedColumns = []; sources: T[] = []; constructor(private readonly registerPropertyDefService: RegisterPropertyDefService<T>, private readonly parent: Alias<T[]>) { } ngOnInit() { this.source$.subscribe((data: T[]) => this.sources = data); this.displayedColumns = this.defaultColumns.map(c => c.id); } findColumnByKey(key: string): DefaultColumn { return this.defaultColumns.find(column => column.id === key); } ngAfterViewInit(): void { this.defaultColumns = this.defaultColumns.map(column => Object.assign(column, { template: this.registerPropertyDefService.getTemplate(this.parent as ComponentInstance, column.id) }) ); } }
Sedikit penjelasan, pekerjaan utama dari komponen ini adalah untuk memperkaya definisi struktur data dalam metode
ngAfterViewInit . Di sini, setelah menginisialisasi templat, kami memperbarui model
Kolom default dengan templat.
Di markup, Anda bisa memperhatikan baris berikut -
<ng-container [ngTemplateOutlet]="findColumnByKey(key)?.template || default" [ngTemplateOutletContext]="{ $implicit: source }"></ng-container>
di sini
fitur digunakan untuk melewatkan lingkup (seperti dalam AngularJS) ke markup. Ini memudahkan untuk mendeklarasikan variabel dalam templat mikro kami melalui konstruk
let-my-var tempat data berada.
Gunakan
// app.component.html <lib-card-list [defaultColumns]="defaultColumns" [source$]="sources$"></lib-card-list> <ng-container *libProvidePropertyDefValue="let element; id: 'id'"> {{ element.id }} </ng-container> <ng-container *libProvidePropertyDefValue="let element; id: 'title'"> {{ element.title }} </ng-container>
Menginisialisasi komponen baru kami, dan meneruskan parameter ke sana.
Definisi template melalui
ng-container dan arahan
libProvidePropertyDefValue kami.
Yang paling penting di sini adalah
"Biarkan elemen; id: 'id' ยป
di mana
elemen adalah lingkup template yang sama dengan objek dengan data dari daftar,
id adalah pengidentifikasi template-mikro.
Sekarang saya ingin kembali ke direktif
offerPropertyDefValue , ke metode
ngOnInit ngOnInit(): void { this.container.clear(); ... }
Anda dapat menempatkan template mikro seperti yang ditunjukkan dalam contoh, dan "membersihkannya" dalam arahan, atau mentransfer definisi mereka sepenuhnya di dalam komponen
daftar kartu-lib , oleh karena itu markupnya akan terlihat seperti ini:
<lib-card-list [defaultColumns]="defaultColumns" [source$]="sources$"> <ng-container *libProvidePropertyDefValue="let element; id: 'id'"> {{ element.id }} </ng-container> <ng-container *libProvidePropertyDefValue="let element; id: 'title'"> {{ element.title }} </ng-container> </lib-card-list>
Secara obyektif - use case kedua lebih produktif.
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], providers: [{ provide: Alias, useExisting: forwardRef(() => AppComponent) }] }) export class AppComponent extends Alias<any> { title = 'card-list-example'; defaultColumns: DefaultColumn[] = [ { id: 'id', label: 'ID' }, { id: 'title', label: 'Title' } ]; sources$ = of([ { id: 1, title: 'Hello' }, { id: 2, title: 'World' } ]); }
Semuanya sangat mendasar di sini, satu-satunya hal yang perlu dipertimbangkan adalah:
penyedia: [{sediakan: Alias, useExisting: forwardRef (() => AppComponent)}]
Desain ini diperlukan untuk menghubungkan templat dan komponen yang menggunakannya.
Dalam layanan kami, konstruktor akan menerima komponen komponen AppComponent dari injector.
Opsional
Dalam contoh ini, kami melihat cara membuat komponen, untuk penggunaan berulang dalam proyek Anda, di mana Anda dapat mentransfer template berbeda dengan data, template ini pasti bisa apa saja.
Bagaimana cara meningkatkannya?
Anda dapat menambahkan pagination dari Material Sudut dan memfilter.
// card-list.component.html <mat-paginator [pageSize]="5"showFirstLastButton></mat-paginator>
Pemfilteran dapat diimplementasikan melalui mat-form-field dan, mirip dengan berpindah halaman saat pagination, memperbarui data.
Itu saja. Saya sangat merekomendasikan secara berkala melihat ke kode sumber perpustakaan
sudut / materi, menurut saya ini adalah kesempatan yang baik untuk meningkatkan pengetahuan Anda dalam menciptakan komponen yang fleksibel dan produktif. Terima kasih atas perhatian anda