Angular12/rxjs: 更新模板中的可观察数据并显示模板中的变化
Angular12/rxjs: updating observable data in template and showing changes in template
所以我正在进行实时搜索,根据用户输入获取数据。
示例:
export class MyComponent implements OnInit {
constructor(private httpHandler: HttpHandlerService) { }
defaultSearchParams: SearchParams = { search: 'bla' }
searchParams = new BehaviorSubject<SearchParams>(this.defaultSearchParams);
myData$?: Observable<PageObjects<MyDataObject>>;
ngOnInit(): void {
this.myData$ = this.searchParams.pipe(
debounceTime(300),
switchMap((searchParams: SearchParams) => this.fetchMyData(searchParams)),
);
}
fetchMyData(searchParams: SearchParams): Observable<MyDataObject> {
return this.httpHandler.getMyData(searchParams);
}
}
我的模板显示数据:
<div *ngIf="(myData$ | async) as resultset$">
<div *ngIf="resultset$.elements && resultset$.elements.length">
<div *ngFor="let dataObject of resultset$.elements">
ID: {{dataObject.id}}, name {{dataObject.name}}, status {{dataObject.status}}
</div>
</div>
</div>
好吧,挑战来了:
在我的模板中,我希望能够更改数据(即新名称、新状态)。
在使用像我这样的可观察对象时,如何在模板中反映这些更改的数据?
所需结果的示例:
<div *ngIf="(myData$ | async) as resultset$">
<div *ngIf="resultset$.elements && resultset$.elements.length">
<div *ngFor="let dataObject of resultset$.elements">
ID: {{dataObject.id}}, name {{dataObject.name}}, status {{dataObject.status}}
<button (click)="changeStatus(dataObject)" type="button">Change status</button>
</div>
</div>
</div>
changeStatus(MyDataObject dataObject) {
this.httpHandler.changeStatusOfObject(dataObject, 'newStatus')
.subscribe(newDataObject => //WHERE should I put the newDataObject to, to show the new value in the template???);
}
尝试更改changeStatus
函数,将更改推送到预定义的BehaviorSubject
changes$
,它与searchParams observable结合形成myData$
observable。
您可以尝试以下方法:
changes$ = new BehaviorSubject<MyDataObject[]>([]);
ngOnInit(): void {
this.myData$ = combineLatest([
this.searchParams.pipe(
debounceTime(300),
switchMap((searchParams: SearchParams) =>
this.fetchMyData(searchParams)
)
),
this.changes$,
]).pipe(
map(([data, changes]) => {
if (changes?.length && data?.elements) {
changes.forEach((changedItem) => {
const ndx = data.elements.findIndex(
(item) => item.id === changedItem.id
);
if (ndx !== -1) data.elements[ndx] = changedItem;
});
}
return data;
})
);
}
changeStatus(dataObject: MyDataObject) {
this.httpHandler
.changeStatusOfObject(dataObject, 'newStatus')
// take one value from this observable and then complete it, to avoid memory leak
.pipe(take(1))
.subscribe((newDataObject) => {
// push the newDataObject to the chagnes$ subject.
this.changes$.next([newDataObject]);
});
}
在我看来,您可以使用 BehaviorSubject
来流式传输数据,并在需要更新单个项目时对其进行调整(无需从后端请求数据)。我会这样做:
export class MyComponent implements OnInit {
constructor(private httpHandler: HttpHandlerService) { }
defaultSearchParams: SearchParams = { search: 'bla' }
searchParams = new BehaviorSubject<SearchParams>(this.defaultSearchParams);
myData$ = new BehaviorSubject<PageObjects<MyDataObject> | null>(null);
ngOnInit(): void {
this.searchParams.pipe(
debounceTime(300),
switchMap((searchParams: SearchParams) => this.fetchMyData(searchParams)),
).subscribe(pagedData => myData$.next(pagedData));
}
fetchMyData(searchParams: SearchParams): Observable<MyDataObject> {
return this.httpHandler.getMyData(searchParams);
}
changeStatus(dataObject: MyDataObject) {
this.httpHandler
.changeStatusOfObject(dataObject, 'newStatus')
.subscribe((newDataObject) => {
// because now we have a behavior subject, we can get the last emitted value and use it to change just the updated object
const pagedData = this.myData$.value;
const elements = [...pagedData.elements];
const updatedElementIndex = elements.findIndex(e => e.id === newDataObject.id);
elements[updatedElementIndex] = newDataObject;
this.myData$.next({
...pagedData,
elements
});
});
}
}
所以我正在进行实时搜索,根据用户输入获取数据。
示例:
export class MyComponent implements OnInit {
constructor(private httpHandler: HttpHandlerService) { }
defaultSearchParams: SearchParams = { search: 'bla' }
searchParams = new BehaviorSubject<SearchParams>(this.defaultSearchParams);
myData$?: Observable<PageObjects<MyDataObject>>;
ngOnInit(): void {
this.myData$ = this.searchParams.pipe(
debounceTime(300),
switchMap((searchParams: SearchParams) => this.fetchMyData(searchParams)),
);
}
fetchMyData(searchParams: SearchParams): Observable<MyDataObject> {
return this.httpHandler.getMyData(searchParams);
}
}
我的模板显示数据:
<div *ngIf="(myData$ | async) as resultset$">
<div *ngIf="resultset$.elements && resultset$.elements.length">
<div *ngFor="let dataObject of resultset$.elements">
ID: {{dataObject.id}}, name {{dataObject.name}}, status {{dataObject.status}}
</div>
</div>
</div>
好吧,挑战来了: 在我的模板中,我希望能够更改数据(即新名称、新状态)。
在使用像我这样的可观察对象时,如何在模板中反映这些更改的数据?
所需结果的示例:
<div *ngIf="(myData$ | async) as resultset$">
<div *ngIf="resultset$.elements && resultset$.elements.length">
<div *ngFor="let dataObject of resultset$.elements">
ID: {{dataObject.id}}, name {{dataObject.name}}, status {{dataObject.status}}
<button (click)="changeStatus(dataObject)" type="button">Change status</button>
</div>
</div>
</div>
changeStatus(MyDataObject dataObject) {
this.httpHandler.changeStatusOfObject(dataObject, 'newStatus')
.subscribe(newDataObject => //WHERE should I put the newDataObject to, to show the new value in the template???);
}
尝试更改changeStatus
函数,将更改推送到预定义的BehaviorSubject
changes$
,它与searchParams observable结合形成myData$
observable。
您可以尝试以下方法:
changes$ = new BehaviorSubject<MyDataObject[]>([]);
ngOnInit(): void {
this.myData$ = combineLatest([
this.searchParams.pipe(
debounceTime(300),
switchMap((searchParams: SearchParams) =>
this.fetchMyData(searchParams)
)
),
this.changes$,
]).pipe(
map(([data, changes]) => {
if (changes?.length && data?.elements) {
changes.forEach((changedItem) => {
const ndx = data.elements.findIndex(
(item) => item.id === changedItem.id
);
if (ndx !== -1) data.elements[ndx] = changedItem;
});
}
return data;
})
);
}
changeStatus(dataObject: MyDataObject) {
this.httpHandler
.changeStatusOfObject(dataObject, 'newStatus')
// take one value from this observable and then complete it, to avoid memory leak
.pipe(take(1))
.subscribe((newDataObject) => {
// push the newDataObject to the chagnes$ subject.
this.changes$.next([newDataObject]);
});
}
在我看来,您可以使用 BehaviorSubject
来流式传输数据,并在需要更新单个项目时对其进行调整(无需从后端请求数据)。我会这样做:
export class MyComponent implements OnInit {
constructor(private httpHandler: HttpHandlerService) { }
defaultSearchParams: SearchParams = { search: 'bla' }
searchParams = new BehaviorSubject<SearchParams>(this.defaultSearchParams);
myData$ = new BehaviorSubject<PageObjects<MyDataObject> | null>(null);
ngOnInit(): void {
this.searchParams.pipe(
debounceTime(300),
switchMap((searchParams: SearchParams) => this.fetchMyData(searchParams)),
).subscribe(pagedData => myData$.next(pagedData));
}
fetchMyData(searchParams: SearchParams): Observable<MyDataObject> {
return this.httpHandler.getMyData(searchParams);
}
changeStatus(dataObject: MyDataObject) {
this.httpHandler
.changeStatusOfObject(dataObject, 'newStatus')
.subscribe((newDataObject) => {
// because now we have a behavior subject, we can get the last emitted value and use it to change just the updated object
const pagedData = this.myData$.value;
const elements = [...pagedData.elements];
const updatedElementIndex = elements.findIndex(e => e.id === newDataObject.id);
elements[updatedElementIndex] = newDataObject;
this.myData$.next({
...pagedData,
elements
});
});
}
}