如何在Angular 6中过滤复杂的结构化Json数据 [英] How to filter complex structured Json data in Angular 6
问题描述
我有一个复杂的结构化json数据,需要在Angular 6 App中应用高级过滤.
I have a complex structured json data that needs to be apply an advanced filtering in my Angular 6 App.
[{
"StudentId": 1,
"StudentName": "Student1",
"Sex":"M",
"Programs": [
{
"StudentId": 1,
"ProgramName": "Java",
"ProgramCategory": "Engineering",
"ProgramStatus": "Full Time"
},
{
"StudentId": 1,
"ProgramName": "HR Management 2",
"ProgramCategory": "HR",
"ProgramStatus": "Part Time"
},
{
"StudentId": 1,
"ProgramName": "Accounting 1",
"ProgramCategory": "Finance",
"ProgramStatus": "Full Time"
}
]
},
{
"StudentId": 2,
"StudentName": "Student2",
"Sex":"F",
"Programs": [
{
"StudentId": 2,
"ProgramName": "HR Management 1",
"ProgramCategory": "HR",
"ProgramStatus": "Part Time"
},
{
"StudentId": 2,
"ProgramName": "Accounting 3",
"ProgramCategory": "Finance",
"ProgramStatus": "Full Time"
}
]
},
{
"StudentId": 3,
"StudentName": "Student3",
"Sex":"F",
"Programs": [
{
"StudentId": 3,
"ProgramName": "Java 3",
"ProgramCategory": "Engineering",
"ProgramStatus": "Full Time"
}
]
},
{
"StudentId": 4,
"StudentName": "Student4",
"Sex":"M",
"Programs": [
{
"StudentId": 4,
"ProgramName": "Java 2",
"ProgramCategory": "Engineering",
"ProgramStatus": "Full Time"
},
{
"StudentId": 4,
"ProgramName": "Accounting 2",
"ProgramCategory": "Finance",
"ProgramStatus": "Part Time"
}
]
},
{
"StudentId": 5,
"StudentName": "Student5",
"Sex":"M",
"Programs": [
{
"StudentId": 5,
"ProgramName": "JavaScript",
"ProgramCategory": "Engineering",
"ProgramStatus": "Part Time"
},
{
"StudentId": 5,
"ProgramName": "HR Management 5",
"ProgramCategory": "HR",
"ProgramStatus": "Full Time"
}
]
}]
过滤器选项:
我想在HTML页面中有3个下拉列表以进行过滤:
Filter Options:
I would like to have 3 drop-down list in HTML page to filter by:
- 性别
- ProgramCategory
- ProgramStatus
HTML视图:
UI视图如下所示:
HTML View:
The UI View will look like the below:
当我选择ProgramCategory = 'HR'
和ProgramStatus = 'Part Time'
时,将仅返回2个学生(student1
,student2
).我花了几天时间尝试获得想要的结果,但仍然无法解决.我将此文章用作参考并根据我的数据进行了一些改进,但是返回的数据不正确,请参见下图:
因此,我只需要返回标记的行(row#:1,2
).
When I select ProgramCategory = 'HR'
and ProgramStatus = 'Part Time'
, there will be only 2 students (student1
,student2
) returned. I spend days try to get the result that I want, but still not solve. I use this article as ref and made some improvement based on my data, but the returned data is incorrect, see the image below:
So, I only need the marked rows (row#:1,2
) to be returned.
上图中标记的行#:5被错误地标记.
import { Component, OnInit } from '@angular/core';
import * as _ from 'lodash';
@Component({
selector: 'app-hfo',
templateUrl: './hfo.component.html',
styleUrls: ['./hfo.component.css']
})
export class HfoComponent implements OnInit {
students: any;
filteredStudents: any;
// basic info
Sex: string;
// child info
ProgramCategory: string;
ProgramStatus: string;
// filter by value
filters = { };
constructor() { }
ngOnInit() {
/// get all students
this.students = this.getStudents();
this.setFilters();
}
private setFilters() {
this.filteredStudents = _.filter(this.students, _.conforms(this.filters) );
}
filterMatch(property: string, value: any) {
this.filters[property] = i => i === value;
this.setFilters();
}
filterMatchSub(property: string, childProperty: string, value: any) {
this.filters[property] = val => val.find( child => child[childProperty] === value);
this.setFilters();
}
/// removes filter
removeFilter(property: string) {
delete this.filters[property];
this[property] = null;
this.ProgramCategory = null;
this.ProgramStatus = null;
this.setFilters();
}
private getStudents() {
return JSON.parse(`
[
{
"StudentId": 1,
"StudentName": "Student1",
"Sex":"M",
"Programs": [
{
"StudentId": 1,
"ProgramName": "Java",
"ProgramCategory": "Engineering",
"ProgramStatus": "Full Time"
},
{
"StudentId": 1,
"ProgramName": "HR Management 2",
"ProgramCategory": "HR",
"ProgramStatus": "Part Time"
},
{
"StudentId": 1,
"ProgramName": "Accounting 1",
"ProgramCategory": "Finance",
"ProgramStatus": "Full Time"
}
]
},
{
"StudentId": 2,
"StudentName": "Student2",
"Sex":"F",
"Programs": [
{
"StudentId": 2,
"ProgramName": "HR Management 1",
"ProgramCategory": "HR",
"ProgramStatus": "Part Time"
},
{
"StudentId": 2,
"ProgramName": "Accounting 3",
"ProgramCategory": "Finance",
"ProgramStatus": "Full Time"
}
]
},
{
"StudentId": 3,
"StudentName": "Student3",
"Sex":"F",
"Programs": [
{
"StudentId": 3,
"ProgramName": "Java 3",
"ProgramCategory": "Engineering",
"ProgramStatus": "Full Time"
}
]
},
{
"StudentId": 4,
"StudentName": "Student4",
"Sex":"M",
"Programs": [
{
"StudentId": 4,
"ProgramName": "Java 2",
"ProgramCategory": "Engineering",
"ProgramStatus": "Full Time"
},
{
"StudentId": 4,
"ProgramName": "Accounting 2",
"ProgramCategory": "Finance",
"ProgramStatus": "Part Time"
}
]
},
{
"StudentId": 5,
"StudentName": "Student5",
"Sex":"M",
"Programs": [
{
"StudentId": 5,
"ProgramName": "JavaScript",
"ProgramCategory": "Engineering",
"ProgramStatus": "Part Time"
},
{
"StudentId": 5,
"ProgramName": "HR Management 5",
"ProgramCategory": "HR",
"ProgramStatus": "Full Time"
}
]
}
]
`);
}
}
我的HTML代码:
<div class="row">
<div class="col-sm-12">
<div class="panel panel-sm ">
<div class="panel-body">
<h5>Basic Info</h5>
<div class="hs-lead">
<div class="row">
<div class="col-sm-3">
<div class="form-group">
<label for="exampleSelect1">Sex</label>
<div class="row">
<div class="col-sm-9">
<select class="form-control" [(ngModel)]="Sex" (change)="filterMatch('Sex', Sex)">
<option value="M">M</option>
<option value="F">F</option>
</select>
</div>
<div class="col-sm-3">
<button class="btn btn-primary" *ngIf="Sex" (click)="removeFilter('Sex')">
Clear
</button>
</div>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="form-group">
<label for="exampleSelect1">ProgramCategory</label>
<div class="row">
<div class="col-sm-9">
<select class="form-control" [(ngModel)]="ProgramCategory" (change)="filterMatchSub('Programs', 'ProgramCategory', ProgramCategory)">
<option value="Engineering">Engineering</option>
<option value="HR">HR</option>
<option value="Finance">Finance</option>
</select>
</div>
<div class="col-sm-3">
<button class="btn btn-primary" *ngIf="ProgramCategory" (click)="removeFilter('Programs')">
Clear
</button>
</div>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="form-group">
<label for="exampleSelect1">ProgramStatus</label>
<div class="row">
<div class="col-sm-9">
<select class="form-control" [(ngModel)]="ProgramStatus" (change)="filterMatchSub('Programs', 'ProgramStatus', ProgramStatus)">
<option value="Full Time">Full Time</option>
<option value="Part Time">Part Time</option>
</select>
</div>
<div class="col-sm-3">
<button class="btn btn-primary" *ngIf="ProgramStatus" (click)="removeFilter('Programs')">
Clear
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="panel panel-xl">
<div class="panel-body">
<h5>Result
<span class="badge badge-info badge-pill pull-right">{{ filteredStudents.length }}</span>
</h5>
<div class="hs-lead">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Sex</th>
<th>Programs</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of filteredStudents ">
<td>{{item.StudentId }}</td>
<td>{{item.StudentName }}</td>
<td>{{item.Sex}}</td>
<td>
{{item.Programs.length}}
<ol *ngFor="let obj of item.Programs">
<li>{{obj.ProgramCategory}} / {{obj.ProgramStatus}}</li>
</ol>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
帮助:
有人可以帮助我实现我的目标吗?
Help:
Could anyone help me to achieve my goal?
您可以更改我当前的ts代码或使用新的解决方案,欢迎两者!
You can change my current ts code or have a new solution, both are welcomed!
非常感谢!
推荐答案
在这里,我使用了响应式表单和rxjs BehaviorSubjects为您提供了一个解决方案:
I've got a solution for you here using reactive forms and rxjs BehaviorSubjects:
https://stackblitz.com/edit/how-to-filter-complex-json-data-new-chind-array-object-xtlbxy
该链接具有完整的解决方案,但这是我认为您遇到的过滤问题的核心:
That link has your full solution, but here's the core of the filtering issue I think you had:
private setFilters() {
this.filteredStudents$.next(this.students$.value);
combineLatest(
this.students$,
this.sexFilterControl.valueChanges,
this.programControls.valueChanges,
this.courseControls.valueChanges
)
.subscribe(([students, sexFilter, programFilters, courseFilters]) => {
let filteredStudents = [ ... students ];
if (sexFilter) {
filteredStudents = filteredStudents.filter(student => student.Sex === sexFilter);
}
// programs
filteredStudents = filteredStudents.filter(student => {
return student.Programs.reduce((programsPrev, program) => {
return programsPrev || Object.entries(programFilters).reduce((filterPrev, [filterName, filterValue]) => {
if (!filterValue) {
return filterPrev;
}
return filterPrev && program[filterName] === filterValue;
}, true);
}, false)
});
// courses
filteredStudents = filteredStudents.filter(student => {
return student.Courses.reduce((coursesPrev, course) => {
return coursesPrev || Object.entries(courseFilters).reduce((filterPrev, [filterName, filterValue]) => {
if (!filterValue) {
return filterPrev;
}
return filterPrev && course[filterName] === filterValue;
}, true);
}, false)
});
this.filteredStudents$.next(filteredStudents);
});
this.sexFilterControl.setValue('');
this.programCategoryFilterControl.setValue('');
this.programStatusFilterControl.setValue('');
this.courseCategoryFilterControl.setValue('');
this.courseStatusFilterControl.setValue('');
}
对ProgramCategory和ProgramStatus(两者都必须针对同一Program进行匹配)的筛选是与分别对二者进行筛选的根本不同的筛选器.
Filtering for both ProgramCategory and ProgramStatus (where both have to match for the same Program) is a fundamentally different filter than filtering for either separately.
由于您对两个程序过滤器的要求实质上是仅显示具有至少一个与所有现有过滤器匹配的程序的学生",因此您可以在堆栈闪电中看到将相关控件分组为FormGroup
并编写反映此预期行为的过滤器.
Since you what you want with your two program filters is essentially "only show students who have at least 1 Program that matches all existing filters", you can see in my stack blitz that I group the relevant controls into a FormGroup
and write filters that reflect this intended behavior.
如果您愿意,我建议您调整表格以使用
If you're up for it, I'd recommend adjusting your table to using @angular/cdk/table
, I'm actually working on an article on that topic now with the guy from Angular Firebase (like in the link you posted). I think that'd be well-worth the effort, especially if you like this more rxjs-centric approach I used in this solution.
这篇关于如何在Angular 6中过滤复杂的结构化Json数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!