Angular 角度材质树不显示填充阵列的子元素
我试图在用户展开节点时动态加载子节点 问题是当我填充子数组时,mat tree没有显示子数组。如果我使用simple*ngFor显示相同的数据,当子数组添加了元素时,它会显示它们 我这里有一个工作示例: 这是代码和html tsAngular 角度材质树不显示填充阵列的子元素,angular,tree,angular-material-6,Angular,Tree,Angular Material 6,我试图在用户展开节点时动态加载子节点 问题是当我填充子数组时,mat tree没有显示子数组。如果我使用simple*ngFor显示相同的数据,当子数组添加了元素时,它会显示它们 我这里有一个工作示例: 这是代码和html ts 从'@angular/cdk/tree'导入{NestedTreeControl}; 从'@angular/core'导入{Component}; 从“@angular/material/tree”导入{MatTreeNestedDataSource}; 导出类Prop
从'@angular/cdk/tree'导入{NestedTreeControl};
从'@angular/core'导入{Component};
从“@angular/material/tree”导入{MatTreeNestedDataSource};
导出类PropertyLevel{
建造师(
公共代码:string,
公共级别:布尔,
公共子属性:PropertyLevel[]
){}
}
@组成部分({
选择器:“我的应用程序”,
templateUrl:“./app.component.html”,
样式URL:['./app.component.css']
})
导出类AppComponent{
名称='角度';
nestedTreeControl:nestedTreeControl;
嵌套数据源:MatTreeNestedDataSource;
构造函数(){
this.nestedTreeControl=新的nestedTreeControl(this.\u getChildren);
this.nestedDataSource=new MatTreeNestedDataSource();
this.nestedDataSource.data=[
新的PropertyLevel('123',false,[]),
新PropertyLevel('345',true[
新的PropertyLevel('345.a',false,null),
新的PropertyLevel('345.b',true,[]),
]),
新的PropertyLevel('567',false,[]),
];
}
hasNestedChild=(uz:number,nodeData:PropertyLevel)=>nodeData.subproperties;
private _getChildren=(节点:PropertyLevel)=>node.subproperties;
expandToggle(节点:PropertyLevel,isExpanded:boolean):void{
if(node.subperties&&node.subperties.length==0){
如果(node.code==“123”){
node.subproperties.push(新的PropertyLevel('123.a',false,null))
}
else if(node.code==“567”){
node.subproperties.push(新的PropertyLevel('567.a',false,null));
node.subproperties.push(newpropertyLevel('567.b',false,null));
node.subproperties.push(新的PropertyLevel('567.c',false,null));
}
}
}
}
html
{{node.code}
{{nestedTreeControl.isExpanded(节点)-'expand_more':'chevron_right'}
{{node.code}
-
{{node.code}}
-
{{subnode.code}
这个故事的寓意(如果我错了,请纠正我)是,在Angularjs和Angularjs中,或者至少在材质树中,开发人员必须提供更改事件,而不是自动连接所有内容的更改检测,这减少了大量幕后对象的创建,使Angular更快、更精简
因此,解决方案不是为子对象使用数组,而是使用BehaviorSubject,并向类添加一个方法以添加子对象
我回到了嵌套节点树()示例(),调整了FileNode类并添加了addChild和addChildren方法
export class FileNode {
kids: FileNode[] = [];
children:BehaviorSubject<FileNode[]> = new BehaviorSubject<FileNode[]>(this.kids);
filename: string;
type: any;
addChild(node:FileNode):void {
this.kids.push(node);
this.children.next(this.kids);
}
addchildren(nodes:FileNode[]) {
this.kids = this.kids.concat(this.kids, nodes);
this.children.next(this.kids);
}
}
现在,Material Tree确实检测到了我在孩子身上的变化,并进行了自我更新。
工作示例
完整、更新的ts文件:
import {NestedTreeControl} from '@angular/cdk/tree';
import {Component, Injectable} from '@angular/core';
import {MatTreeNestedDataSource} from '@angular/material/tree';
import {BehaviorSubject} from 'rxjs';
/**
* Json node data with nested structure. Each node has a filename and a value or a list of children
*/
export class FileNode {
kids: FileNode[] = [];
children:BehaviorSubject<FileNode[]> = new BehaviorSubject<FileNode[]>(this.kids);
filename: string;
type: any;
addChild(node:FileNode):void {
this.kids.push(node);
this.children.next(this.kids);
}
addchildren(nodes:FileNode[]) {
this.kids = this.kids.concat(this.kids, nodes);
this.children.next(this.kids);
}
}
/**
* The Json tree data in string. The data could be parsed into Json object
*/
const TREE_DATA = JSON.stringify({
Applications: {
Calendar: 'app',
Chrome: 'app',
Webstorm: 'app'
},
Documents: {
angular: {
src: {
compiler: 'ts',
core: 'ts'
}
},
material2: {
src: {
button: 'ts',
checkbox: 'ts',
input: 'ts'
}
}
},
Downloads: {
October: 'pdf',
November: 'pdf',
Tutorial: 'html'
},
Pictures: {
'Photo Booth Library': {
Contents: 'dir',
Pictures: 'dir'
},
Sun: 'png',
Woods: 'jpg'
}
});
/**
* File database, it can build a tree structured Json object from string.
* Each node in Json object represents a file or a directory. For a file, it has filename and type.
* For a directory, it has filename and children (a list of files or directories).
* The input will be a json object string, and the output is a list of `FileNode` with nested
* structure.
*/
@Injectable()
export class FileDatabase {
dataChange = new BehaviorSubject<FileNode[]>([]);
get data(): FileNode[] { return this.dataChange.value; }
constructor() {
this.initialize();
}
initialize() {
// Parse the string to json object.
const dataObject = JSON.parse(TREE_DATA);
// Build the tree nodes from Json object. The result is a list of `FileNode` with nested
// file node as children.
const data = this.buildFileTree(dataObject, 0);
// Notify the change.
this.dataChange.next(data);
}
/**
* Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
* The return value is the list of `FileNode`.
*/
buildFileTree(obj: {[key: string]: any}, level: number): FileNode[] {
return Object.keys(obj).reduce<FileNode[]>((accumulator, key) => {
const value = obj[key];
const node = new FileNode();
node.filename = key;
if (value != null) {
if (typeof value === 'object') {
node.addchildren(this.buildFileTree(value, level + 1));
} else {
node.type = value;
}
}
return accumulator.concat(node);
}, []);
}
addPictureFile():void {
var picNode = this.data.find((node) => node.filename == 'Pictures');
var newNode = new FileNode();
newNode.filename = 'foo';
newNode.type = 'gif';
picNode.addChild(newNode);
}
}
/**
* @title Tree with nested nodes
*/
@Component({
selector: 'tree-nested-overview-example',
templateUrl: 'tree-nested-overview-example.html',
styleUrls: ['tree-nested-overview-example.css'],
providers: [FileDatabase]
})
export class TreeNestedOverviewExample {
nestedTreeControl: NestedTreeControl<FileNode>;
nestedDataSource: MatTreeNestedDataSource<FileNode>;
constructor(private database: FileDatabase) {
this.nestedTreeControl = new NestedTreeControl<FileNode>(this._getChildren);
this.nestedDataSource = new MatTreeNestedDataSource();
database.dataChange.subscribe(data => {
this.nestedDataSource.data = data;
}
);
}
hasNestedChild = (_: number, nodeData: FileNode) => !nodeData.type;
private _getChildren = (node: FileNode) => node.children;
}
从'@angular/cdk/tree'导入{NestedTreeControl};
从“@angular/core”导入{Component,Injectable};
从“@angular/material/tree”导入{MatTreeNestedDataSource};
从“rxjs”导入{BehaviorSubject};
/**
*具有嵌套结构的Json节点数据。每个节点都有一个文件名和一个值或子节点列表
*/
导出类文件节点{
子节点:FileNode[]=[];
children:BehaviorSubject=新的BehaviorSubject(this.children);
文件名:字符串;
类型:任意;
addChild(节点:FileNode):无效{
this.kids.push(节点);
这个。孩子。下一个(这个。孩子);
}
addchildren(节点:FileNode[]){
this.kids=this.kids.concat(this.kids,节点);
这个。孩子。下一个(这个。孩子);
}
}
/**
*字符串形式的Json树数据。数据可以解析为Json对象
*/
const TREE_DATA=JSON.stringify({
应用程序:{
日历:“应用程序”,
Chrome:“应用程序”,
Webstorm:“应用程序”
},
文件:{
角度:{
src:{
编译器:“ts”,
核心:“ts”
}
},
材料2:{
src:{
按钮:“ts”,
复选框:“ts”,
输入:“ts”
}
}
},
下载:{
十月:"pdf",,
十一月:"pdf",,
教程:“html”
},
图片:{
‘摄影棚图书馆’:{
目录:'dir',
图片:“dir”
},
孙:"png",,
伍兹:“jpg”
}
});
/**
*文件数据库,它可以从字符串构建树结构的Json对象。
*Json对象中的每个节点表示一个文件或一个目录。对于文件,它具有文件名和类型。
*对于目录,它有文件名和子目录(文件或目录的列表)。
*输入为json对象字符串,输出为带有嵌套
*结构。
*/
@可注射()
导出类文件数据库{
dataChange=新的行为主体([]);
get data():FileNode[]{返回this.dataChange.value;}
构造函数(){
这是初始化();
}
初始化(){
//将字符串解析为json对象。
const dataObject=JSON.parse(树数据);
//从Json对象构建树节点。结果是带有嵌套
//文件节点作为子节点。
const data=this.buildFileTree(数据对象,0);
//通知变更。
this.dataChange.next(数据);
}
/
import {NestedTreeControl} from '@angular/cdk/tree';
import {Component, Injectable} from '@angular/core';
import {MatTreeNestedDataSource} from '@angular/material/tree';
import {BehaviorSubject} from 'rxjs';
/**
* Json node data with nested structure. Each node has a filename and a value or a list of children
*/
export class FileNode {
kids: FileNode[] = [];
children:BehaviorSubject<FileNode[]> = new BehaviorSubject<FileNode[]>(this.kids);
filename: string;
type: any;
addChild(node:FileNode):void {
this.kids.push(node);
this.children.next(this.kids);
}
addchildren(nodes:FileNode[]) {
this.kids = this.kids.concat(this.kids, nodes);
this.children.next(this.kids);
}
}
/**
* The Json tree data in string. The data could be parsed into Json object
*/
const TREE_DATA = JSON.stringify({
Applications: {
Calendar: 'app',
Chrome: 'app',
Webstorm: 'app'
},
Documents: {
angular: {
src: {
compiler: 'ts',
core: 'ts'
}
},
material2: {
src: {
button: 'ts',
checkbox: 'ts',
input: 'ts'
}
}
},
Downloads: {
October: 'pdf',
November: 'pdf',
Tutorial: 'html'
},
Pictures: {
'Photo Booth Library': {
Contents: 'dir',
Pictures: 'dir'
},
Sun: 'png',
Woods: 'jpg'
}
});
/**
* File database, it can build a tree structured Json object from string.
* Each node in Json object represents a file or a directory. For a file, it has filename and type.
* For a directory, it has filename and children (a list of files or directories).
* The input will be a json object string, and the output is a list of `FileNode` with nested
* structure.
*/
@Injectable()
export class FileDatabase {
dataChange = new BehaviorSubject<FileNode[]>([]);
get data(): FileNode[] { return this.dataChange.value; }
constructor() {
this.initialize();
}
initialize() {
// Parse the string to json object.
const dataObject = JSON.parse(TREE_DATA);
// Build the tree nodes from Json object. The result is a list of `FileNode` with nested
// file node as children.
const data = this.buildFileTree(dataObject, 0);
// Notify the change.
this.dataChange.next(data);
}
/**
* Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
* The return value is the list of `FileNode`.
*/
buildFileTree(obj: {[key: string]: any}, level: number): FileNode[] {
return Object.keys(obj).reduce<FileNode[]>((accumulator, key) => {
const value = obj[key];
const node = new FileNode();
node.filename = key;
if (value != null) {
if (typeof value === 'object') {
node.addchildren(this.buildFileTree(value, level + 1));
} else {
node.type = value;
}
}
return accumulator.concat(node);
}, []);
}
addPictureFile():void {
var picNode = this.data.find((node) => node.filename == 'Pictures');
var newNode = new FileNode();
newNode.filename = 'foo';
newNode.type = 'gif';
picNode.addChild(newNode);
}
}
/**
* @title Tree with nested nodes
*/
@Component({
selector: 'tree-nested-overview-example',
templateUrl: 'tree-nested-overview-example.html',
styleUrls: ['tree-nested-overview-example.css'],
providers: [FileDatabase]
})
export class TreeNestedOverviewExample {
nestedTreeControl: NestedTreeControl<FileNode>;
nestedDataSource: MatTreeNestedDataSource<FileNode>;
constructor(private database: FileDatabase) {
this.nestedTreeControl = new NestedTreeControl<FileNode>(this._getChildren);
this.nestedDataSource = new MatTreeNestedDataSource();
database.dataChange.subscribe(data => {
this.nestedDataSource.data = data;
}
);
}
hasNestedChild = (_: number, nodeData: FileNode) => !nodeData.type;
private _getChildren = (node: FileNode) => node.children;
}