Hallo an alle.
Jeder, der über das Angular-Framework schrieb, stieß irgendwie auf die
Angular-Material- Bibliothek (oder arbeitete sogar damit). Dies ist eine sehr gut geschriebene Komponentenbibliothek mit flexiblem Design, die durch die Möglichkeit implementiert wird, verschiedene Themen für Ihre Anwendung mit einer großen Anzahl von Komponenten für alle Gelegenheiten zu erstellen.
In meiner täglichen Arbeit kann kein einziges Projekt darauf verzichten.
Neben all den Vorteilen der Flexibilität dieser Bibliothek ist es auch möglich, auf die Erfahrung der Entwickler beim Schreiben ihrer eigenen Komponenten zurückzugreifen. Dies ist für mich das beste Handbuch für die Best-Practice-Entwicklung in
Angular .
In diesem Artikel möchte ich Ihnen
mitteilen, wie Sie den Ansatz mit einer komplexen Vorlage implementieren können, die im
MatTableModule- Modul implementiert ist.
Als Beispiel möchte ich zeigen, wie eine
Liste von Karten mit der Möglichkeit zum Hinzufügen von Paginierung und Filtern erstellt wird. Als Grundlage verwenden wir das Modellmodell der MatTable-Komponente.
Vorlage (
Quelle ):
<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>
Nach dem Studium der Vorlage wird deutlich, dass wir in den
ng-container- Tags das Markup für eine bestimmte Spalte der Tabelle angeben, aber wie funktioniert es im Inneren? Diese Frage stellte ich, als ich dieses Design sah, teilweise aufgrund der Tatsache, dass ich nicht mit dynamischen Komponenten arbeitete. Und so fangen wir an
(Quellcode) .
Struktur
Eine Reihe von Entitäten, die wir erstellen müssen. Dieses Blockdiagramm veranschaulicht ihre Wechselwirkung.

Erster Schritt
Wir benötigen einen Service, um unsere Mikrovorlagen zu registrieren.
@Injectable() export class RegisterPropertyDef<T> {
Zweiter Schritt
Erstellen Sie eine Direktive zum Registrieren von Vorlagen:
@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();
Schritt drei
Erstellen Sie eine Komponente:
@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) }) ); } }
Eine kleine Erklärung: Die Hauptaufgabe der Komponente besteht darin, die Definition der Datenstruktur in der
ngAfterViewInit- Methode zu erweitern. Hier aktualisieren wir nach dem Initialisieren der Vorlagen die
defaultColumns- Modelle
mit Vorlagen.
Im Markup können Sie auf die folgenden Zeilen achten:
<ng-container [ngTemplateOutlet]="findColumnByKey(key)?.template || default" [ngTemplateOutletContext]="{ $implicit: source }"></ng-container>
Hier wird eine
Funktion verwendet, um den Bereich (wie in AngularJS) an das Markup zu übergeben. Dies macht es bequem, eine Variable in unseren Mikrovorlagen über das
let-my-var- Konstrukt zu deklarieren, in dem die Daten liegen werden.
Verwenden Sie
// 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>
Initialisierung unserer neuen Komponente und Übergabe von Parametern an diese.
Vorlagendefinition über
ng-container und unsere
libProvidePropertyDefValue- Direktive.
Das Wichtigste hier ist
"Element lassen; id: 'id' »
Dabei ist
Element der Bereich der Vorlage, der dem Objekt mit den Daten aus der Liste entspricht.
id ist die Kennung der Mikrovorlage.
Jetzt möchte ich zur Anweisung
deployPropertyDefValue und zur Methode
ngOnInit zurückkehren ngOnInit(): void { this.container.clear(); ... }
Sie können Mikrovorlagen wie im Beispiel gezeigt platzieren und sie in der Direktive „bereinigen“ oder ihre Definition vollständig in die
lib-card-list- Komponente übertragen. Daher sieht das Markup folgendermaßen aus:
<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>
Objektiv - der zweite Anwendungsfall ist produktiver.
@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' } ]); }
Hier ist alles ziemlich elementar, das einzige, was zu beachten ist, ist:
Anbieter: [{bereitstellen: Alias, useExisting: forwardRef (() => AppComponent)}]
Dieses Design ist erforderlich, um die Vorlage und die Komponente, die sie verwendet, zu verknüpfen.
In unserem Service erhält der Konstruktor eine Instanz der AppComponent-Komponente vom Injektor.
Optional
In diesem Beispiel haben wir uns angesehen, wie Sie eine Komponente für die wiederholte Verwendung in Ihren Projekten erstellen, für die Sie verschiedene Vorlagen mit Daten übertragen können. Diese Vorlagen können definitiv alles sein.
Wie kann man sich verbessern?
Sie können Paginierung aus Winkelmaterial und Filterung hinzufügen.
// card-list.component.html <mat-paginator [pageSize]="5"showFirstLastButton></mat-paginator>
Das Filtern kann über ein Mat-Form-Feld implementiert werden und ähnlich wie beim Wechseln der Seiten während der Paginierung Daten aktualisiert werden.
Das ist alles. Ich empfehle dringend, regelmäßig den Quellcode der
Winkel- / Materialbibliothek zu überprüfen. Meiner Meinung nach ist dies eine gute Gelegenheit, Ihr Wissen bei der Erstellung flexibler und produktiver Komponenten zu erweitern. Vielen Dank für Ihre Aufmerksamkeit.