[英]Refresh Data in Fully Reactive Way using angular async pipe
問這個我覺得有點尷尬,但我確定我錯過了一些東西。 花了很長時間尋找/研究,但只通過mergeMap等提出需要主題或行為主題或pipe等的復雜而復雜的解決方案。
我正在從組件 Z4C4AD5FCA2E7A3F74DBB1CED00381AA4 中的命令式 HTTP 可觀察方法(手動訂閱)轉變為反應式 HTTP 可觀察方法(可觀察 $ | async)。
當頁面初始化時,讓它與異步 pipe 一起工作沒有問題。
但是,“即時”刷新模板的最佳方式是什么,即我知道我已將一條記錄插入到基礎數據庫中,並希望這反映在我的模板中。
使用命令式方法,我可以簡單地再次訂閱(例如從單擊處理程序)並將結果重新分配給與 ngfor 指令關聯的屬性,並且更改檢測將觸發並更新 DOM。
我想出的唯一解決方案是簡單地再次從服務中獲取 observable,這會觸發更改檢測。
只是為了讓這里更清楚,這是一個代碼的應用(非常簡單,只需專注於解決方案)。
必須有一個簡單的解決方案?
<div *ngIf="(model$ | async)?.length > 0" class="card mb-3 shadow">
<div class="card-header">Actions Notes</div>
<div class="card-body">
<mat-list *ngFor="let action of model$ | async">
<div class="small">
<strong>
{{action.createdDate | date:'dd/MM/yyyy HH:mm'}}
{{action.createdBy}}
</strong>
</div>
<div>{{action.notes}}</div>
</mat-list>
</div>
</div>
<button (click)="onClick()">name</button>
model$: Observable<any>;
constructor(
private changeTransferActionsService: ChangeTransferActionsService
) {}
ngOnInit(): void {
this.getObservable();
}
private getObservable(): void {
this.model$ = this.changeTransferActionsService
.getActionsWithNotesById(this.model.id)
.pipe(
map((x) => x.result),
share()
);
}
onClick(): void {
//insert row into database here (via HTTP post service call)
this.getObservable();
}
只是跟進我最初的問題,這里有三個帶有解釋的工作解決方案。 感謝所有回復的人。
使用具有任意類型值和初始值的 RxJS BehaviorSubject
。 使用展平運算符SwitchMap
將其與更高階的 observable 結合起來。
當單擊按鈕時, BehaviourSubject
發出(下一個)一個值(在本例中為 0),該值被饋送到 HTTP 可觀察對象(忽略傳遞的值),從而導致內部可觀察對象發出一個值並啟動更改檢測。
當使用 BehaviourSubject 時,observable 會在訂閱時發出一個值。
在組件 HTML 模板中。
<button (click)="onClick()">Click Me</button>
在組件 Typescript 中。
import { BehaviorSubject, Observable } from 'rxjs';
.
.
.
subject = new BehaviorSubject(0);
.
.
.
this.model$ = this.subject.asObservable().pipe(
// startWith(0),
switchMap(() => this.changeTransferActionsService
.getActionsWithNotesById(this.model.id)
.pipe(
map((x) => x.result)
)));
.
.
.
onClick(): void {
this.subject.next(0);
}
使用 RxJS Subject
將其與使用展平運算符SwitchMap
的高階可觀察對象結合起來。
當單擊按鈕時, Subject
發出(下一個)一個值(在這種情況下未定義),該值被饋送到 HTTP 可觀察對象(忽略傳遞的值),從而導致內部可觀察對象發出一個值並啟動更改檢測。
Subject 需要startWith(0)
否則當組件首次初始化並首次被異步 pipe 訂閱時,observable 不會發出任何值。
import {Observable, Subject } from 'rxjs';
.
.
.
subject = new Subject();
.
.
.
this.model$ = this.subject.asObservable().pipe(
startWith(0),
switchMap(() => this.changeTransferActionsService
.getActionsWithNotesById(this.model.id)
.pipe(
map((x) => x.result)
)));
.
.
.
onClick(): void {
this.subject.next(0);
}
<button (click)="onClick()">Click Me</button>
請注意,在上述兩種情況下,可以直接在 HTML 組件模板中調用主題的下一個方法,因此主題具有公共訪問修飾符(無論如何都是默認值)。 這樣就不需要在 Typescript 中使用 onCLick 方法。
<button (click)="subject.next(0)">Click Me</button>
正如hanan所建議的那樣
將模板變量名稱添加到按鈕並使用@ViewChild
獲取元素引用。 使用fromEvent
創建一個 RxJS 可觀察對象,然后使用switchMap
高階可觀察對象方法。
.
.
.
@ViewChild('refreshButton') refreshButton: ElementRef;
.
.
.
ngAfterViewInit(): void {
const click$ = fromEvent(this.refreshButton.nativeElement, 'click');
this.model$ = click$.pipe(
startWith(0),
switchMap(_ => {
return this.changeTransferActionsService
.getActionsWithNotesById(this.model.id)
.pipe(
map((x) => x.result),
);
}));
}
.
.
.
<button #refreshButton>Click Me</button>
.
.
.
總之,如果用戶通過單擊隨意啟動刷新,則解決方案 3 最有意義,因為它不需要引入“虛擬”主題。
但是,主題方法是有意義的,如果可觀察對象應該以編程方式從任何地方發出......通過簡單地調用主題的下一個方法,可觀察對象可以根據命令發出值。
您知道,這個主題還有更多內容,真正的反應式方法可能會開始您關於使用共享可注入 singleton 服務和基於主題/行為主題/異步主題等的訂閱的思考過程。 不過,這些都是架構和設計決策。 感謝您的所有建議。
您可以從其單擊事件中獲取刷新 Btn 參考並創建 stream,然后使用虛擬條目啟動它。 現在你有一個刷新 stream,然后像這樣切換到你的數據 stream:
零件
@ViewChild('refreshButton') refreshBtn:ElementRef;
model$: Observable<any>;
ngAfterViewInit() {
let click$ = fromEvent(this.refreshBtn.nativeElement, 'click')
this.model$ = click$.pipe(startWith(0),
switchMap(_=> {
return this.changeTransferActionsService
.getActionsWithNotesById(this.model.id)
.pipe(
map((x) => x.result),
);
}));
}
模板
而不是使用 2 個異步管道,只使用 1 個和ngLet
它將簡化您的模板並且您不必使用share()
:
<button #refreshButton >Refresh</button>
<ng-container *ngLet="(model$ | async) as model">
<div *ngIf="model.length > 0" class="card mb-3 shadow">
<div class="card-header">Actions Notes</div>
<div class="card-body">
<mat-list *ngFor="let action of model">
<div class="small">
<strong>
{{action.createdDate | date:'dd/MM/yyyy HH:mm'}}
{{action.createdBy}}
</strong>
</div>
<div>{{action.notes}}</div>
</mat-list>
</div>
</div>
<button (click)="onClick()">name</button>
</ng-container>
但是要務實,不要花費太多精力來使其完全反應。 您之前的解決方案也很好,您只需要改進您的模板。
注意:在這種情況下,您可以使用*ngIf
代替*ngLet
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.