如何过滤mat-tree组件Angular Material 6.0.1
How to filter a mat-tree component Angular Material 6.0.1
我正在使用 mat-tree angular material 组件。
这是一个不错的组件,具有一些非常有用的功能,例如 multi-select、全部展开 all/collapse。
我无法在他们的任何 API 中找到任何树过滤功能。
有没有人遇到过这个功能或做了任何工作来获得 mat-tree 过滤器?
首先在视图中添加一个输入作为过滤器。将 keyup 事件绑定到 rxjs Subject
<input type="text" matInput placeholder="search" #filter (keyup)="keyEvent.next($event)" [(ngModel)]="keyword">
然后查询您的后端以使用关键字过滤树节点
this.keyEvent.pipe(
map((e: any) => e.target.value.toLowerCase()),
debounceTime(500),
distinctUntilChanged(),
switchMap((keyword: string) => {
if (keyword && keyword.length > 2) {
return this.yourservice.searchForData(this.entId, keyword);
} else {
return of();
}
})
)
.subscribe((r) => {
this.nestedDataSource.data = r;
this.nestedTreeControl.dataNodes = r;
this.nestedTreeControl.expandAll();
});
在我花了几天时间完成同一任务后,这里有一些我可以提供的提示:
我正在使用输入事件来跟随用户输入:
<input matInput class="form-control"
(input)="filterChanged($event.target.value)"
placeholder="Search Skill">
我在这个过滤器上附加了一个主题,这样我就可以订阅它了:
searchFilter: Subject<string> = new Subject<string>();
filterChanged(filter: string): void {
this.searchFilter.next(filter);
}
为了让用户顺畅,通常,我们希望延迟搜索执行,您可以使用 debounceTime
。
this.searchFilter.pipe(debounceTime(500), distinctUntilChanged())
.subscribe(value => {
if (value && value.length >= 3) {
this.filterByName(value);
} else {
this.clearFilter();
}
});
为了执行搜索,我使用 css class 隐藏和显示节点。这是直接在扁平且非常容易过滤的演示文稿集合上完成的。
treeControl: FlatTreeControl<SkillFlatNode>;
this.treeControl.dataNodes
首先,我隐藏所有内容,然后仅显示符合条件的内容。最后,我想展示他们的parents,但这是我的树结构特有的。
private filterByName(term: string): void {
const filteredItems = this.treeControl.dataNodes.filter(
x => x.value.DisplayName.toLowerCase().indexOf(term.toLowerCase()) === -1
);
filteredItems.map(x => {
x.visible = false;
});
const visibleItems = this.treeControl.dataNodes.filter(
x => x.value.IsSkill &&
x.value.DisplayName.toLowerCase().indexOf(term.toLowerCase()) > -1
);
visibleItems.map( x => {
x.visible = true;
this.markParent(x);
});
}
最后,这里是清除过滤器:
private clearFilter(): void {
this.treeControl.dataNodes.forEach(x => x.visible = true);
}
不要像我一样犯同样的错误并尝试过滤输入集合(this.dataSource.data
在我的例子中),因为你会失去你的选择或者你将不得不将它映射回演示文稿。
这是我的初始数据:
this.treeFlattener = new MatTreeFlattener(
this.transformer, this._getLevel, this._isExpandable, this._getChildren
);
this.treeControl = new FlatTreeControl<SkillFlatNode>(
this._getLevel, this._isExpandable
);
this.dataSource = new MatTreeFlatDataSource(
this.treeControl, this.treeFlattener
);
skillService.dataChange.subscribe(data => {
this.dataSource.data = data;
});
我通过创建一个新的数据源(过滤)解决了这个问题。
我来解释一下共享的例子link:我在ChecklistDatabase
中用filter(filterText: string)
过滤了数据,触发了一个dataChange
事件。然后 datasource.data
被 TreeChecklistExample
中的处理事件更改。于是修改了数据源
filter(filterText: string) {
let filteredTreeData;
if (filterText) {
filteredTreeData = this.treeData.filter(
//There is filter function in the sample
);
} else {
filteredTreeData = this.treeData;
}
// file node as children.
const data = this.buildFileTree(filteredTreeData, '0');
// Notify the change. !!!IMPORTANT
this.dataChange.next(data);
}
我可以使用简单的递归来过滤树。以下是代码片段:
在 input type="text"
的 (keyup)
上调用了 filter()
函数。 cloneDeep
函数是从 lodash 导入的 import * as cloneDeep from 'lodash/cloneDeep';
this.searchString
是过滤器文本的字符串值。
filter() {
const clonedTreeLocal = cloneDeep(this.clonedTree);
this.recursiveNodeEliminator(clonedTreeLocal);
this.dataSource.data = clonedTreeLocal;
this.treeControl.expandAll();
}
树形结构由接口定义
export interface ITreeDataStructure {
Id?: number;
name: string;
type: string;
children?: Array<ITreeDataStructure>;
}
实际过滤是由函数recursiveNodeEliminator
完成的
recursiveNodeEliminator(tree: Array<ITreeDataStructure>): boolean {
for (let index = tree.length - 1; index >= 0; index--) {
const node = tree[index];
if (node.children) {
const parentCanBeEliminated = this.recursiveNodeEliminator(node.children);
if (parentCanBeEliminated) {
if (node.name.toLocaleLowerCase().indexOf(this.searchString.toLocaleLowerCase()) === -1) {
tree.splice(index, 1);
}
}
} else {
// Its a leaf node. No more branches.
if (node.name.toLocaleLowerCase().indexOf(this.searchString.toLocaleLowerCase()) === -1) {
tree.splice(index, 1);
}
}
}
return tree.length === 0;
}
这是模型过滤器。创建 2 个列表,一个用于存储所有数据,另一个用作数据源。
SearchCategory(searchText){
this.searchText=searchText;
this.categories=this.categoryNameSearch(this.AllCategories,searchText)
this.dataSource.data = this.categories;
}
categoryNameSearch(categorys:Category[],searchText):Category[]{
let category:Category[];
category=categorys.filter(f=>this.converter(f.name).includes(this.converter(searchText)))
categorys.forEach(element => {
this.categoryNameSearch(element.childrens,searchText).forEach(e=>category.push(e))
});
return category;
}
converter(text) {
var trMap = {
'çÇ':'c',
'ğĞ':'g',
'şŞ':'s',
'üÜ':'u',
'ıİ':'i',
'öÖ':'o'
};
for(var key in trMap) {
text = text.replace(new RegExp('['+key+']','g'), trMap[key]);
}
return text.replace(/[^-a-zA-Z0-9\s]+/ig, '') // remove non-alphanumeric chars
.replace(/\s/gi, "-") // convert spaces to dashes
.replace(/[-]+/gi, "-") // trim repeated dashes
.toLowerCase();
}
Html边
<input type="text" (ngModelChange)="SearchCategory($event)" placeholder="Search Category" class="form-control"/>
Stackblitz link for mat-tree filter
如果有人需要在不修改数据源的情况下直观地过滤 mat 树,请使用此解决方案。
基本上这个想法是隐藏不属于搜索字符串的节点。
输入字段
<input [(ngModel)]="searchString" />
为叶节点调用过滤函数(这是在第一个mat-tree-node中完成的)
<mat-tree-node
*matTreeNodeDef="let node"
[style.display]="
filterLeafNode(node) ? 'none' : 'block'
"
.....
......
对叶节点以外的节点调用过滤函数(在第二个mat-tree-node中完成)
<mat-tree-node
*matTreeNodeDef="let node; when: hasChild"
[style.display]="filterParentNode(node) ? 'none' : 'block'"
.....
.....
filterLeafNode 函数
filterLeafNode(node: TodoItemFlatNode): boolean {
if (!this.searchString) {
return false
}
return node.item.toLowerCase()
.indexOf(this.searchString?.toLowerCase()) === -1
}
filterParentNode 函数
filterParentNode(node: TodoItemFlatNode): boolean {
if (
!this.searchString ||
node.item.toLowerCase()
.indexOf(
this.searchString?.toLowerCase()
) !== -1
) {
return false
}
const descendants = this.treeControl.getDescendants(node)
if (
descendants.some(
(descendantNode) =>
descendantNode.item
.toLowerCase()
.indexOf(this.searchString?.toLowerCase()) !== -1
)
) {
return false
}
return true
}
我正在使用 mat-tree angular material 组件。 这是一个不错的组件,具有一些非常有用的功能,例如 multi-select、全部展开 all/collapse。 我无法在他们的任何 API 中找到任何树过滤功能。 有没有人遇到过这个功能或做了任何工作来获得 mat-tree 过滤器?
首先在视图中添加一个输入作为过滤器。将 keyup 事件绑定到 rxjs Subject
<input type="text" matInput placeholder="search" #filter (keyup)="keyEvent.next($event)" [(ngModel)]="keyword">
然后查询您的后端以使用关键字过滤树节点
this.keyEvent.pipe(
map((e: any) => e.target.value.toLowerCase()),
debounceTime(500),
distinctUntilChanged(),
switchMap((keyword: string) => {
if (keyword && keyword.length > 2) {
return this.yourservice.searchForData(this.entId, keyword);
} else {
return of();
}
})
)
.subscribe((r) => {
this.nestedDataSource.data = r;
this.nestedTreeControl.dataNodes = r;
this.nestedTreeControl.expandAll();
});
在我花了几天时间完成同一任务后,这里有一些我可以提供的提示: 我正在使用输入事件来跟随用户输入:
<input matInput class="form-control"
(input)="filterChanged($event.target.value)"
placeholder="Search Skill">
我在这个过滤器上附加了一个主题,这样我就可以订阅它了:
searchFilter: Subject<string> = new Subject<string>();
filterChanged(filter: string): void {
this.searchFilter.next(filter);
}
为了让用户顺畅,通常,我们希望延迟搜索执行,您可以使用 debounceTime
。
this.searchFilter.pipe(debounceTime(500), distinctUntilChanged())
.subscribe(value => {
if (value && value.length >= 3) {
this.filterByName(value);
} else {
this.clearFilter();
}
});
为了执行搜索,我使用 css class 隐藏和显示节点。这是直接在扁平且非常容易过滤的演示文稿集合上完成的。
treeControl: FlatTreeControl<SkillFlatNode>;
this.treeControl.dataNodes
首先,我隐藏所有内容,然后仅显示符合条件的内容。最后,我想展示他们的parents,但这是我的树结构特有的。
private filterByName(term: string): void {
const filteredItems = this.treeControl.dataNodes.filter(
x => x.value.DisplayName.toLowerCase().indexOf(term.toLowerCase()) === -1
);
filteredItems.map(x => {
x.visible = false;
});
const visibleItems = this.treeControl.dataNodes.filter(
x => x.value.IsSkill &&
x.value.DisplayName.toLowerCase().indexOf(term.toLowerCase()) > -1
);
visibleItems.map( x => {
x.visible = true;
this.markParent(x);
});
}
最后,这里是清除过滤器:
private clearFilter(): void {
this.treeControl.dataNodes.forEach(x => x.visible = true);
}
不要像我一样犯同样的错误并尝试过滤输入集合(this.dataSource.data
在我的例子中),因为你会失去你的选择或者你将不得不将它映射回演示文稿。
这是我的初始数据:
this.treeFlattener = new MatTreeFlattener(
this.transformer, this._getLevel, this._isExpandable, this._getChildren
);
this.treeControl = new FlatTreeControl<SkillFlatNode>(
this._getLevel, this._isExpandable
);
this.dataSource = new MatTreeFlatDataSource(
this.treeControl, this.treeFlattener
);
skillService.dataChange.subscribe(data => {
this.dataSource.data = data;
});
我通过创建一个新的数据源(过滤)解决了这个问题。
我来解释一下共享的例子link:我在ChecklistDatabase
中用filter(filterText: string)
过滤了数据,触发了一个dataChange
事件。然后 datasource.data
被 TreeChecklistExample
中的处理事件更改。于是修改了数据源
filter(filterText: string) {
let filteredTreeData;
if (filterText) {
filteredTreeData = this.treeData.filter(
//There is filter function in the sample
);
} else {
filteredTreeData = this.treeData;
}
// file node as children.
const data = this.buildFileTree(filteredTreeData, '0');
// Notify the change. !!!IMPORTANT
this.dataChange.next(data);
}
我可以使用简单的递归来过滤树。以下是代码片段:
在 input type="text"
的 (keyup)
上调用了 filter()
函数。 cloneDeep
函数是从 lodash 导入的 import * as cloneDeep from 'lodash/cloneDeep';
this.searchString
是过滤器文本的字符串值。
filter() {
const clonedTreeLocal = cloneDeep(this.clonedTree);
this.recursiveNodeEliminator(clonedTreeLocal);
this.dataSource.data = clonedTreeLocal;
this.treeControl.expandAll();
}
树形结构由接口定义
export interface ITreeDataStructure {
Id?: number;
name: string;
type: string;
children?: Array<ITreeDataStructure>;
}
实际过滤是由函数recursiveNodeEliminator
recursiveNodeEliminator(tree: Array<ITreeDataStructure>): boolean {
for (let index = tree.length - 1; index >= 0; index--) {
const node = tree[index];
if (node.children) {
const parentCanBeEliminated = this.recursiveNodeEliminator(node.children);
if (parentCanBeEliminated) {
if (node.name.toLocaleLowerCase().indexOf(this.searchString.toLocaleLowerCase()) === -1) {
tree.splice(index, 1);
}
}
} else {
// Its a leaf node. No more branches.
if (node.name.toLocaleLowerCase().indexOf(this.searchString.toLocaleLowerCase()) === -1) {
tree.splice(index, 1);
}
}
}
return tree.length === 0;
}
这是模型过滤器。创建 2 个列表,一个用于存储所有数据,另一个用作数据源。
SearchCategory(searchText){
this.searchText=searchText;
this.categories=this.categoryNameSearch(this.AllCategories,searchText)
this.dataSource.data = this.categories;
}
categoryNameSearch(categorys:Category[],searchText):Category[]{
let category:Category[];
category=categorys.filter(f=>this.converter(f.name).includes(this.converter(searchText)))
categorys.forEach(element => {
this.categoryNameSearch(element.childrens,searchText).forEach(e=>category.push(e))
});
return category;
}
converter(text) {
var trMap = {
'çÇ':'c',
'ğĞ':'g',
'şŞ':'s',
'üÜ':'u',
'ıİ':'i',
'öÖ':'o'
};
for(var key in trMap) {
text = text.replace(new RegExp('['+key+']','g'), trMap[key]);
}
return text.replace(/[^-a-zA-Z0-9\s]+/ig, '') // remove non-alphanumeric chars
.replace(/\s/gi, "-") // convert spaces to dashes
.replace(/[-]+/gi, "-") // trim repeated dashes
.toLowerCase();
}
Html边
<input type="text" (ngModelChange)="SearchCategory($event)" placeholder="Search Category" class="form-control"/>
Stackblitz link for mat-tree filter
如果有人需要在不修改数据源的情况下直观地过滤 mat 树,请使用此解决方案。
基本上这个想法是隐藏不属于搜索字符串的节点。
输入字段
<input [(ngModel)]="searchString" />
为叶节点调用过滤函数(这是在第一个mat-tree-node中完成的)
<mat-tree-node
*matTreeNodeDef="let node"
[style.display]="
filterLeafNode(node) ? 'none' : 'block'
"
.....
......
对叶节点以外的节点调用过滤函数(在第二个mat-tree-node中完成)
<mat-tree-node
*matTreeNodeDef="let node; when: hasChild"
[style.display]="filterParentNode(node) ? 'none' : 'block'"
.....
.....
filterLeafNode 函数
filterLeafNode(node: TodoItemFlatNode): boolean {
if (!this.searchString) {
return false
}
return node.item.toLowerCase()
.indexOf(this.searchString?.toLowerCase()) === -1
}
filterParentNode 函数
filterParentNode(node: TodoItemFlatNode): boolean {
if (
!this.searchString ||
node.item.toLowerCase()
.indexOf(
this.searchString?.toLowerCase()
) !== -1
) {
return false
}
const descendants = this.treeControl.getDescendants(node)
if (
descendants.some(
(descendantNode) =>
descendantNode.item
.toLowerCase()
.indexOf(this.searchString?.toLowerCase()) !== -1
)
) {
return false
}
return true
}