Мне немного неловко спрашивать об этом, но я уверен, что что-то упускаю. Потратил много времени на поиски / исследования, но придумываю только сложные и запутанные решения, требующие субъектных или поведенческих объектов или конвейера через mergeMap, и т. Д. реактивный HTTP-наблюдаемый подход (наблюдаемый $ | asyn c) в шаблоне компонента HTML.
Нет проблем с его работой с конвейером asyn c при инициализации страницы.
Однако как лучше всего обновить sh шаблон «на лету», т.е. я знаю, что вставил запись в базовую базу данных и хочу, чтобы это отразилось в моем шаблоне.
При императивном подходе я мог бы просто снова подписаться (например, из обработчика кликов) и переназначить результат свойству, связанному с директивой ngfor, и обнаружение изменений сработало бы и DOM обновился.
Единственное решение, которое у меня есть придумать - просто снова получить наблюдаемое из службы, и это вызывает обнаружение изменений.
Чтобы было понятнее, вот фрагмент кода (очень простой, чтобы сосредоточиться на решении).
Должно быть простое решение?
<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();
}
Просто продолжая свой первоначальный вопрос, вот три рабочих решения с пояснениями. Спасибо всем, кто ответил.
Решение 1 - BehaviourSubject / SwitchMap
Используйте Rx JS BehaviorSubject
с произвольным значением типа и начальным значением. Объедините это с наблюдаемым более высокого порядка, используя оператор сглаживания SwitchMap
.
При нажатии кнопки BehaviourSubject
испускает (следующее) значение (в данном случае 0), которое передается наблюдаемому HTTP ( переданное значение игнорируется), в результате чего внутренний наблюдаемый объект испускает значение и срабатывает обнаружение изменений.
При использовании BehaviourSubject наблюдаемый объект выдает значение при подписке.
В компоненте 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);
}
Решение 2 - Тема / SwitchMap / initialValue
Используйте Rx JS Subject
Объедините это с наблюдаемым более высокого порядка с использованием оператора сглаживания SwitchMap
.
При нажатии кнопки Subject
выдает (следующее) значение (в данном случае не определено), которое передается наблюдаемому HTTP (переданное значение игнорируется), что приводит к тому, что внутренний наблюдаемый объект испускает значение, и срабатывает обнаружение изменений.
Субъект требует startWith(0)
, иначе наблюдаемое не будет выдавать никаких значений, когда компонент сначала инициализируется и сначала подписывается с помощью asyn c pipe.
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 субъект имел модификатор доступа publi c (в любом случае по умолчанию). Это избавило бы от необходимости иметь метод onCLick в Typescript.
<button (click)="subject.next(0)">Click Me</button>
Решение 3 - fromEvent
Как было предложено hanan
Добавьте имя переменной шаблона к кнопке и используйте @ViewChild
, чтобы получить ссылку на элемент. Создайте наблюдаемый Rx JS, используя fromEvent
, а затем используйте 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 имеет наибольший смысл, если refre sh является запускаться пользователем по желанию с помощью щелчка, поскольку для этого не требовалось вводить «фиктивный» объект.
Тем не менее, субъектный подход имеет смысл в том, должен ли наблюдаемый излучать программно из любого места ... просто вызвав следующий метод по этому предмету, наблюдаемое может выдавать значения по команде.
Вы знаете, в этом топе c есть гораздо больше, и действительно реактивный подход может начать ваш мыслительный процесс об использовании общих вводимых одноэлементных сервисов и подписок, основанных на предметах / поведенческих объектах / асинхронных объектах и так далее. Но это архитектурные и дизайнерские решения. Спасибо за все ваши предложения.