我正在尝试为一个项目创建带有复选框的材料树视图。我已经在stackover流程中寻找了类似的实现,但是不幸的是我没有获得任何成功。我希望这将成为其他尝试这样做的人的资源!
我当前正在阅读/实现的文档来自https://material.angular.io/components/tree/examples。
我正在使用Angular 7
和ASP.NET Core 3.0
。
问题:
我能够用ASP.NET
返回的JSON结果填充节点,但是不幸的是,数组位置以及ID和名称都被填充,这不是期望的结果。我要返回的是带有ID的名称。
即:0: {outterId: "101", name: "Groceries", innerArray1: [{…}, {…}]}
和
1: {outterId: "201", name: "Groceries1", innerArray1: [{…}, {…}]}
例如,在Angular 7 Material documentation
中,他们具有以下JSON
结构:
const TREE_DATA = {
Groceries: {
'Almond Meal flour': null,
'Organic eggs': null,
'Protein Powder': null,
Fruits: {
Apple: null,
Berries: ['Blueberry', 'Raspberry'],
Orange: null
}
},
Reminders: [
'Cook dinner',
'Read the Material Design spec',
'Upgrade Application to Angular'
]
};
我从JSON
返回的ASP.NET
给了我一些嵌套的JSON
:
[
{
"outterId": 201,
"outterName": "Groceries",
"innerArray1": [
{
"innerArray1Id": 212,
"innerName1": "Almond Meal Flour"
"innerArray2": [
{
"innerArray2Id": 313,
"innerName2": "Almond Meal bread",
},
{
"innerArray2Id": 414,
"innerName2": "Almond Milk",
}
]
},
{
"innerArray1Id": 11111,
"innerName1": "Fruits",
"innerArray2": [
{
"innerArray2Id": 1414,
"innerName2": "Apple",
},
{
"innerArray2Id": 9616,
"innerName2": "Berries",
}
]
}
]
}
]
[
{
"outterId": 211,
"outterName": "Groceries2",
"innerArray1": [
{
"innerArray1Id": 213,
"innerName1": "Organic eggs"
"innerArray2": [
{
"innerArray2Id": 314,
"innerName2": "Egg nog",
},
{
"innerArray2Id": 415,
"innerName2": "Egg mixtures",
}
]
},
{
"innerArray1Id": 11112,
"innerName1": "Fruits",
"innerArray2": [
{
"innerArray2Id": 1415,
"innerName2": "Orange",
},
{
"innerArray2Id": 9617,
"innerName2": "Raspberry",
}
]
}
]
}
]
我得到的是这样的:
0 +
201
Groceries
innerArray1 +
201 +
212
Almond Meal Flour
innerArray2 +
313
Almond Meal bread
414
Almond Milk
.
.
etc.
1 +
212
Groceries2
innerArray1 +
213 +
Organic eggs
innerArray2 +
314
Egg nog
415
Egg mixtures
.
.
etc.
我想要的是:
Groceries +
Almond Meal Flour +
Almond Meal Bread
Almond Milk
Fruits +
Apple
Berries
.
.
etc.
GroceryService
groceryList() {
return this.http.get(this.url + 'produce/groceries');
}
ts文件
export class TodoItemNode {
innerArray1: TodoItemNode[];
innerArray2: TodoItemNode[];
outterName: string;
outterId: number;
innerArray1Id: number;
innerName1: string;
innerArray2Id: number;
innerName2: string;
}
export class TodoItemFlatNode {
item: string;
level: number;
expandable: boolean;
}
@Component({
selector: 'tsFiletest',
templateUrl: './tsFiletest.component.html',
styleUrls: ['./tsFiletest.component.css'],
})
export class TSFileTest implements OnInit{
dataChange = new BehaviorSubject<TodoItemNode[]>([]);
get data(): TodoItemNode[] { return this.dataChange.value; }
//This is where the actual building of the tree begins.
buildFileTree(obj: {[key: string]: any}, level: number): TodoItemNode[] {
return Object.keys(obj).reduce<TodoItemNode[]>((accumulator, key) => {
const value = obj[key];
const node = new TodoItemNode();
node.outterName = key; //The array position i.e. 0 and 1 is actually assigned here.
if (value != null) {
if (typeof value === 'object') {
node.innerArray1 = this.buildFileTree(value, level + 1);
//node.innerArray2 = this.buildFileTree(value, level + 1); //I'm not sure if I should be building the inner array2 here.
} else {
node.outterName = value;
}
}
return accumulator.concat(node);
}, []);
}
insertItem(parent: TodoItemNode, name: string) {
if (parent.innerArray1) {
parent.innerArray1.push({outterName: name} as TodoItemNode);
this.dataChange.next(this.data);
}
}
updateItem(node: TodoItemNode, name: string) {
node.outterName = name;
this.dataChange.next(this.data);
}
flatNodeMap = new Map<TodoItemFlatNode, TodoItemNode>();
nestedNodeMap = new Map<TodoItemNode, TodoItemFlatNode>();
selectedParent: TodoItemFlatNode | null = null;
newItemName = '';
treeControl: FlatTreeControl<TodoItemFlatNode>;
treeFlattener: MatTreeFlattener<TodoItemNode, TodoItemFlatNode>;
dataSource: MatTreeFlatDataSource<TodoItemNode, TodoItemFlatNode>;
checklistSelection = new SelectionModel<TodoItemFlatNode>(true /* multiple */);
constructor(private _database: ChecklistDatabase, private groceryService: GroceryService) {
this.treeFlattener = new MatTreeFlattener(this.transformer, this.getLevel,
this.isExpandable, this.getChildren);
this.treeControl = new FlatTreeControl<TodoItemFlatNode>(this.getLevel, this.isExpandable);
this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
this.getGroceryList(); //initialize
this.dataChange.subscribe(data => {
this.dataSource.data = data;
});
}
groceryListResponse: any = [];
groceryList: TodoItemNode[];
ngOnInit() {
this.getGroceryList();
}
getGroceryList() {
return this.groceryService.groceryList().subscribe(res => {
this.groceryListResponse = res;
this.groceryList = this.groceryListResponse;
const data = this.buildFileTree(this.groceryList, 0);
// Notify the change.
this.dataChange.next(data);
});
}
getLevel = (node: TodoItemFlatNode) => node.level;
isExpandable = (node: TodoItemFlatNode) => node.expandable;
getChildren = (node: TodoItemNode): TodoItemNode[] => node.innerArray1;
hasChild = (_: number, _nodeData: TodoItemFlatNode) => _nodeData.expandable;
hasNoContent = (_: number, _nodeData: TodoItemFlatNode) => _nodeData.item === '';
transformer = (node: TodoItemNode, level: number) => {
const existingNode = this.nestedNodeMap.get(node);
const flatNode = existingNode && existingNode.item === node.outterName
? existingNode
: new TodoItemFlatNode();
flatNode.item = node.outterName;
flatNode.level = level;
flatNode.expandable = !!node.innerArray1;
this.flatNodeMap.set(flatNode, node);
this.nestedNodeMap.set(node, flatNode);
return flatNode;
}
descendantsAllSelected(node: TodoItemFlatNode): boolean {
const descendants = this.treeControl.getDescendants(node);
const descAllSelected = descendants.every(child =>
this.checklistSelection.isSelected(child)
);
return descAllSelected;
}
descendantsPartiallySelected(node: TodoItemFlatNode): boolean {
const descendants = this.treeControl.getDescendants(node);
const result = descendants.some(child => this.checklistSelection.isSelected(child));
return result && !this.descendantsAllSelected(node);
}
todoItemSelectionToggle(node: TodoItemFlatNode): void {
this.checklistSelection.toggle(node);
const descendants = this.treeControl.getDescendants(node);
this.checklistSelection.isSelected(node)
? this.checklistSelection.select(...descendants)
: this.checklistSelection.deselect(...descendants);
// Force update for the parent
descendants.every(child =>
this.checklistSelection.isSelected(child)
);
this.checkAllParentsSelection(node);
}
/** Toggle a leaf to-do item selection. Check all the parents to see if they changed */
todoLeafItemSelectionToggle(node: TodoItemFlatNode): void {
this.checklistSelection.toggle(node);
this.checkAllParentsSelection(node);
}
/* Checks all the parents when a leaf node is selected/unselected */
checkAllParentsSelection(node: TodoItemFlatNode): void {
let parent: TodoItemFlatNode | null = this.getParentNode(node);
while (parent) {
this.checkRootNodeSelection(parent);
parent = this.getParentNode(parent);
}
}
/** Check root node checked state and change it accordingly */
checkRootNodeSelection(node: TodoItemFlatNode): void {
const nodeSelected = this.checklistSelection.isSelected(node);
const descendants = this.treeControl.getDescendants(node);
const descAllSelected = descendants.every(child =>
this.checklistSelection.isSelected(child)
);
if (nodeSelected && !descAllSelected) {
this.checklistSelection.deselect(node);
} else if (!nodeSelected && descAllSelected) {
this.checklistSelection.select(node);
}
}
/* Get the parent node of a node */
getParentNode(node: TodoItemFlatNode): TodoItemFlatNode | null {
const currentLevel = this.getLevel(node);
if (currentLevel < 1) {
return null;
}
const startIndex = this.treeControl.dataNodes.indexOf(node) - 1;
for (let i = startIndex; i >= 0; i--) {
const currentNode = this.treeControl.dataNodes[i];
if (this.getLevel(currentNode) < currentLevel) {
return currentNode;
}
}
return null;
}
/** Select the category so we can insert the new item. */
addNewItem(node: TodoItemFlatNode) {
const parentNode = this.flatNodeMap.get(node);
this.insertItem(parentNode!, '');
this.treeControl.expand(node);
}
/** Save the node to database */
saveNode(node: TodoItemFlatNode, itemValue: string) {
const nestedNode = this.flatNodeMap.get(node);
this.updateItem(nestedNode!, itemValue);
}
}