您首先要为每门课程添加排名,然后按此排序。即
addRank(a: Course, query: string): number{
var rank: number;
if(course.course_title.toUpperCase().includes(query)) rank += 5;
if(course.description.toUpperCase().includes(query)) rank += 3;
if(course.keywords.toUpperCase().includes(query)) rank += 1;
course.rank = rank
然后按该排名降序排列:
<li *ngFor="let course of courses | addRank: searchText | orderBy: '-rank'">
但是,Angular(与 AngularJs/Angular 1 不同)不提供 orderBy 或过滤器,他们建议您在组件中自己执行这些操作,而不是使用管道:
Angular 不提供用于过滤或排序列表的管道。
熟悉 AngularJS 的开发人员将它们称为 filter 和 orderBy。
Angular 中没有等价物。
这不是疏忽。 Angular 不提供这样的管道,因为它们
表现不佳并防止激进的缩小。
参考:AngularPipes
考虑到这一点,您可能会使用一个单独的 courseResults 列表,它是 course 数组的过滤和有序版本。当然,您可以在输入任何搜索/过滤条件之前将其初始化为课程。您还可以通过在搜索输入中添加debounce 来尽量减少不必要的搜索/过滤调用(如果您在键入时这样做;如果您在单击按钮时这样做,那么您会跳过它) .
简单的解决方案:
因此,在您的 StackBlitz 的基础上,我完全摆脱了使用管道,创建了一个新的 StackBlitz:
<input type='text' [(ngModel)]='searchText' placeholder=" Search a Topic, Subject, or a Course!" (keyup)="search($event)">
我们不显示所有课程,而是显示 courseResults...:
<ul>
<li *ngFor="let course of courseResults">
...初始化为空或所有课程(如这里):
public courseResults: Course[] = this.courses;
然后搜索可以是:
search($event) {
this.courseResults = this.sortByRank(this.rankAndFilter(this.courses, this.searchText.toUpperCase()));
}
private rankAndFilter(courses: Course[], query: string) {
var result = [];
var rank: number;
for (let course of courses) {
rank = 0;
if(course.course_title.toUpperCase().includes(query)) {rank += 5};
if(course.description.toUpperCase().includes(query)) {rank += 3};
if(course.keywords.toUpperCase().includes(query)) {rank += 1};
course.rank = rank;
if (rank > 0) result.push(course);
}
return result;
}
private sortByRank(courses: Course[]) {
return courses.sort(function(a:Course,b:Course){
return b.rank - a.rank;
});
}
扩展解决方案:
[Edit] 一个新的forked StackBlitz,它使用 debounce(如果用户键入速度非常快,则不会对每个按键进行整个过滤和排序)和 distinctUntilChanged(如果用户退格一个字符,它不会t 麻烦),并创建了 Array 的全局扩展(sortBy 和 sortByDesc)。
所以现在我们有了输入:
<input type='text' #searchInput placeholder=" Search a Topic, Subject, or a Course!">
然后将 ViewChild 作为 ElementRef:
@ViewChild('searchInput') searchInputRef: ElementRef;
然后在初始化之后,我们在原生元素的输入上设置 observable 并订阅它:
ngAfterViewInit() {
var subscription = fromEvent(this.searchInputRef.nativeElement, 'input').pipe(
map((e: KeyboardEvent) => (<HTMLInputElement>e.target).value),
debounceTime(400),
distinctUntilChanged()
//switchMap(() => this.performSearch())
);
subscription.subscribe(data => {
this.performSearch(data);
});
}
private performSearch(searchText) {
console.log("Search: " + searchText);
this.searchText = searchText;
this.courseResults = this.rankAndFilter(this.courses, searchText).sortByDesc('rank');
return this.courseResults;
}