В настоящее время я пытаюсь построить иерархическое представление пользователей. Моя конечная цель - создать представление, используя это представление иерархии или что-то подобное.
Сложность заключается в том, как задаются объекты JSON, используемые для создания иерархии. Это пример ответа (этот ответ может быть намного больше), где pid — это идентификатор родителя, а глубина — это расстояние от первого родителя.
response = [
{uid: "abc", pid: null, depth: 1, parent: true},
{uid: "def", pid: "abc", depth: 2, parent: false},
{uid: "ghi", pid: "abc", depth: 2, parent: true},
{uid: "jkl", pid: "ghi", depth: 3, parent: false},
{uid: "mno", pid: "ghi", depth: 3, parent: false},
]
Чтобы лучше объяснить приведенный выше ответ, вот его визуальная иерархия: изображение
Многие ответы и решения, которые я видел до сих пор, используют JSON с дочерними элементами, вложенными в каждый из них. Можно ли сгенерировать представление, используя приведенную выше модель json?
Любая помощь или понимание будут высоко оценены! Спасибо!
Да, мне нужно получить схему. Как тот, который я связал в первом абзаце.
Вы можете использовать редуктор для преобразования плоского массива в карту узлов UID, и, получив карту, вы можете легко заполнить дочерние элементы. Затем вы можете просто выбрать корневой узел и использовать его для рендеринга HTML.
const map = [
{uid: "abc", pid: null, depth: 1, parent: true},
{uid: "def", pid: "abc", depth: 2, parent: true},
{uid: "ghi", pid: "abc", depth: 2, parent: false},
{uid: "jkl", pid: "ghi", depth: 3, parent: false},
{uid: "mno", pid: "ghi", depth: 3, parent: false},
].reduce((acc, node) => (node.children = [], acc[node.uid] = node, acc), {});
const [root] = Object.values(map)
.map(node => (node.pid && map[node.pid].children.push(node), node))
.filter(node => node.pid === null);
console.info(root);
Вы можете визуализировать дерево, рекурсивно используя один и тот же компонент, и шаблон будет отображать дочерние элементы.
@Component({
selector: 'app-node',
template: `
<span>Node</span>
<app-node [node] = "child" *ngFor = "let child of node.children"></app-node>
`
})
export class NodeComponent {
@Input()
public node: any;
}
Нетрудно изменить приведенное выше, чтобы оно соответствовало HTML/CSS, на который вы ссылались в своем вопросе.
Во-первых, вам нужно преобразовать вашу таблицу ссылок на себя в иерархическую таблицу (дерево). Я предлагаю вам использовать нестандартная труба для этого, так как вы сможете повторно использовать этот канал в других местах.
Вы можете использовать код Reactgular, мой код из темы Переполнение стека или написать свой собственный код. Я создаю свой канал converter
с кодом Reactgular:
конвертер.труба.ц
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'converter'
})
export class ConverterPipe implements PipeTransform {
transform(array: any[], id: string = 'uid', parentId: string = 'pid'): any[] {
const map = array.reduce(
(acc, node) => ((node.items = []), (acc[node[id]] = node), acc),
{}
);
return Object.values(map)
.map(
node => (node[parentId] && map[node[parentId]].items.push(node), node)
)
.filter(node => node[parentId] === null);
}
}
Не забудьте добавить его в раздел declaration
вашего модуля:
app.module.ts
import { ConverterPipe } from './converter.pipe';
@NgModule({
declarations: [
ConverterPipe
]
})
export class AppModule { }
Теперь вы можете создать свой шаблон компонента и использовать подход из Представление иерархии CodePen. Поскольку вам нужна разная разметка для ветвей и листьев, удобно использовать NgTemplateOutlet
и NgIf
структурные директивы. Это хорошая идея — переместить разметку уровня в шаблон и повторно использовать ее, когда вам нужно отобразить дерево в Angular. Та же идея проиллюстрирована в моем отвечать. На основе предоставленного кода CodePen ваша разметка Angular может выглядеть следующим образом:
app.component.html
<div class = "hv-wrapper">
<ng-template #Item let-item>
<ng-container *ngIf = "!item.items.length; else Component">
<p>{{ item.uid }}</p>
</ng-container>
<ng-template #Component>
<div class = "hv-item">
<div class = "hv-item-parent">
<p>{{ item.uid }}</p>
</div>
<div class = "hv-item-children">
<div class = "hv-item-child" *ngFor = "let child of item.items">
<ng-container
*ngTemplateOutlet = "Item; context: { $implicit: child }"
></ng-container>
</div>
</div>
</div>
</ng-template>
</ng-template>
<ng-container *ngFor = "let child of response | converter"
><ng-container
*ngTemplateOutlet = "Item; context: { $implicit: child }"
></ng-container
></ng-container>
</div>
Здесь response
— ваш исходный массив:
app.component.ts
export class AppComponent {
response = [
{ uid: 'abc', pid: null, depth: 1, parent: true },
{ uid: 'def', pid: 'abc', depth: 2, parent: true },
{ uid: 'ghi', pid: 'abc', depth: 2, parent: false },
{ uid: 'jkl', pid: 'ghi', depth: 3, parent: false },
{ uid: 'mno', pid: 'ghi', depth: 3, parent: false }
];
}
Не забудьте использовать стили CodePen SASS в своем проекте.
После этого вы получите график вида:
Это Проект StackBlitz, демонстрирующий этот подход в действии.
Хорошо. Я правильно понимаю, что вам нужно получить диаграмму (график) из ваших данных?