Estou desenvolvendo várias bibliotecas Angular, então adoro criar soluções simples e facilmente reutilizáveis para desenvolvedores. Recentemente, um de meus seguidores do Twitter me perguntou como fazer um componente que exibisse seus dados em uma árvore hierárquica - visualização em árvore.
Adoro tarefas como essas porque permitem abranger muitos casos de uso diferentes com um mínimo de lógica interna. Neste artigo, descreverei como penso ao resolver esses problemas.
Isenção de responsabilidade: este tutorial é destinado a um público de alunos do Angular. Se você entender como fazer um tipo recursivo, um componente recursivo e converter os dados passados pela função de manipulador nele, pode pular.
Então, o que precisamos?
Em primeiro lugar, precisamos entender com quais dados trabalharemos. O que descreve essa estrutura de árvore?
Aqui, a primeira coisa que vem à mente é um array multidimensional: se encontrarmos um elemento nele, simplesmente o mostraremos. Se encontrarmos uma matriz aninhada, mergulharemos um nível abaixo.
Vamos descrever esse tipo no TypeScript:
export type MultidimensionalArray<string> =
| string
| ReadonlyArray<MultidimensionalArray<string>>;TypeScript recursive type references :
readonly items: MultidimensionalArray<string> = [
"Hello",
["here", "is", ["some", "structured"], "Data"],
"Bye"
];(«» («» («» …)))… !
, ? . , , , .
export type MultidimensionalArray<T> =
| T
| ReadonlyArray<MultidimensionalArray<T>>;- !
Angular-
Angular . tree view, , .
tree view:
—
, isArray , HostBinding, .
@Component({
selector: "m-dimensional-view",
templateUrl: "./m-dimensional-view.template.html",
styleUrls: ["./m-dimensional-view.styles.less"],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MultidimensionalViewComponent<T> {
@Input()
value: MultidimensionalArray<T> = [];
@HostBinding("class._array")
get isArray(): boolean {
return Array.isArray(this.value);
}
} isArray- *ngIf
, *ngFor m-dimensional-view , — .
, , .
<ng-container *ngIf="isArray; else itemView">
<m-dimensional-view
*ngFor="let item of value"
[value]="item"
></m-dimensional-view>
</ng-container>
<ng-template #itemView>
{{ value }}
</ng-template>, , .
:host {
display: block;
&._array {
margin-left: 20px;
}
}margin-left , LESS
, :
toString ( {{value}} ).
, , toString-. , [object Object]
— -. , - . : « ?».
:
@Component({})
export class MultidimensionalViewComponent<T> {
// ...
@Input()
stringify: (item: T) => string = (item: T) => String(item);
// ...
}, . String.
:
<ng-container *ngIf="isArray; else itemView">
<m-dimensional-view
*ngFor="let item of value"
[stringify]="stringify"
[value]="item"
></m-dimensional-view>
</ng-container>
<ng-template #itemView>
{{stringify(value)}}
</ng-template>stringify , , .
. : , , , .
Waterplea CSS-, :
…
?
ng-polymorheus. , .
npm i @tinkoff/ng-polymorpheus
«», «», «», «». :
import { PolymorpheusContent } from "@tinkoff/ng-polymorpheus";
// ...
@Component({
selector: "m-dimensional-view",
templateUrl: "./m-dimensional-view.template.html",
styleUrls: ["./m-dimensional-view.styles.less"],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MultidimensionalViewComponent<T> {
@Input()
value: MultidimensionalArray<T> = [];
@Input()
content: PolymorpheusContent = "";
@HostBinding("class._array")
get isArray(): boolean {
return Array.isArray(this.value);
}
}stringify polymorpheus-outlet. . , . — , , context .
. :
readonly itemsWithIcons: MultidimensionalArray<Node> = [
{
title: "Documents",
icon: "https://www.flaticon.com/svg/static/icons/svg/210/210086.svg"
},
[
{
title: "hello.doc",
icon: "https://www.flaticon.com/svg/static/icons/svg/2306/2306060.svg"
},
{
title: "table.csv",
icon: "https://www.flaticon.com/svg/static/icons/svg/2306/2306046.svg"
}
]
];polymorheus , :
<m-dimensional-view
[value]="itemsWithIcons"
[content]="itemView"
></m-dimensional-view>
<ng-template #itemView let-icon="icon" let-title="title">
<img alt="icon" width="16" [src]="icon" />
{{title}}
</ng-template> , tree view . let-icon , ng-template. :
ng-polymorheus: Stackblitz
, , HTML. , , , .
, , , .