I am developing several Angular libraries, so I love making simple and easily reusable solutions for developers. Recently, one of my Twitter followers asked me how to make a component that would display its data in a hierarchical tree - tree view.
I love these kinds of problems because they provide an opportunity to cover many different use cases with a minimum amount of logic inside. In this article, I will describe how I think when I solve such problems.
Disclaimer: This tutorial is intended for an audience of Angular learners. If you understand how to make a recursive type, a recursive component and convert the data passed by the handler function in it, you can skip it.
So what do we need?
, . ?
: , . , .
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. , , , .
, , , .