我目前正在尝试构建用户的层次结构视图。我的最终目标是使用this hierarchy view生成视图 或类似的东西。
困难在于如何给定用于生成层次结构的JSON对象。这是一个示例响应(此响应可能更大),其中pid是父代id,而depth是到第一个父代的距离。
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},
]
为更好地解释上述响应,这是它的视觉层次结构视图: image
到目前为止,我看到的许多答案和解决方案都使用JSON,并且每个子级中都嵌套有子级。是否可以使用上述json模型生成视图?
任何帮助或见识将不胜感激!谢谢!
答案 0 :(得分:3)
您可以使用化简器将平面数组转换为节点的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.log(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相匹配并不难。
答案 1 :(得分:3)
首先,您需要将自引用表转换为分层表(树)。我建议您使用custom pipe来执行此操作,因为您将能够在其他地方重用此管道。
您可以使用Reactgular的代码,我在StackOverflow线程中的代码,或编写自己的代码。我使用Reactgular的代码创建converter
管道:
converter.pipe.ts
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 { }
现在,您可以创建组件模板并使用Hierarchy View CodePen中的方法。由于您需要对分支和叶子使用不同的标记,因此使用NgTemplateOutlet
's和NgIf
structural directives很方便。在需要在Angular中渲染树时,将级别标记移动到模板中并重新使用它是一个好主意。我的answer中也说明了相同的想法。根据提供的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 project,演示了此方法的实际作用。