我正在使用 Angular 17 编写一个电子商务应用程序。
我有一个产品列表,我在我的一个 HTML 组件文件中使用来自 Angular/core 的 @for 块进行迭代。
这是我的代码的过于简化的版本:
@for(product of products; track product){
<h3>{{product.name}}</h3>
<span>{{product.price}}</span>
}
我的问题是产品列表可能会因用户输入(通过产品过滤)而发生变化。
如何更改 @for 的 HTML,以便留意列表更改并自动同步,而无需刷新页面?
到目前为止我只找到了有关使用 ngFor 的信息,但我真的很想使用新的 @for 块。
我们可以使用
fromEvent
和一个存储原始值和 rxjs 流的方法,我们使用 input
监听事件 fromEvent
,然后使用 map
来使用 filter
数组修改值方法,最后我们返回值。 fromEvent
最初不会被触发,所以我们使用 merge
和 of
来触发整个数组的初始值!
import { Component, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import { fromEvent, merge, Observable, of } from 'rxjs';
import {
map,
debounceTime,
} from 'rxjs/operators';
import 'zone.js';
export interface Product {
name: string;
price: number;
}
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
template: `
<input type="text" #search/>
@for(product of products | async; track product){
<h3>{{product.name}}</h3>
<span>{{product.price}}</span>
}
`,
})
export class App {
@ViewChild('search') search!: ElementRef<HTMLInputElement>;
name = 'Angular';
searchStr = '';
products!: Observable<Product[]>;
productsOriginal: Product[] = [
{ name: 'test1', price: 12 },
{ name: 'testxcvbxcv2', price: 13 },
{ name: 'xcvb', price: 14 },
{ name: 'wert', price: 15 },
{ name: 'sadfasdf', price: 161 },
{ name: 'asdfasdf', price: 18 },
{ name: 'asdf', price: 119 },
];
ngAfterViewInit() {
// if you are going to change the values of product inside the ngFor
// we need to clone productsOriginal, since updating products might modify
// productsOriginal
const products = structuredClone(this.productsOriginal);
this.products = merge(
// set the initial value of the products array
of(products),
// subscribe to key strokes on search
fromEvent(this.search.nativeElement, 'input').pipe(
debounceTime(500), // prevent event from firing multiple times, take only event with 500ms gap
map((event: any) => {
// get the input search value
const searchKey = event?.target?.value;
// filter the array if it includes the value and return it!
return this.productsOriginal.filter((product: Product) =>
product.name.includes(searchKey)
);
})
)
);
}
}
bootstrapApplication(App);
Stackblitz Demo