У меня есть сервис, который всегда возвращает Observable<T>
, и я не могу изменить код этого сервиса.
Предположим, у меня есть кнопка, и всякий раз, когда она нажимается, я вызываю метод в службе, и он возвращает новый Observable. Как я могу обновить новые данные в пользовательском интерфейсе?
Исходный код и игровая площадка на StackBlitz
app.component.ts
import { Component, Injectable, OnInit } from '@angular/core';
import { Observable, of } from 'rxjs';
@Injectable()
export class Service {
// Cannot change the code in this class
public getRandom(): Observable<number> {
return of(Math.random());
}
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
constructor(private service: Service) {}
public random: number = 0;
public random$: Observable<number> = new Observable<number>();
ngOnInit(): void {
this.random = 0;
}
buttonClick(): void {
// how can I update the random with this.service.getRandom()?
console.log('button clicked')
}
}
app.component.html
<h1>{{random}}</h1>
<button (click)="buttonClick()">Get new Random number</button>
Один простой способ — преобразовать Observable в Promise и использовать await:
async buttonClick(): Promise<void> {
const value = await firstValueFrom(random$);
console.log('button clicked ' + value);
}
Если Observable испускает более одного раза и вам нужны все значения, используйте .subscribe().
Потому что концептуально они Полегче для понимания (у них ровно одно значение) и в 99% случаев достаточный (поскольку HTTP-запросы дают ровно одно значение). Вдобавок отписываться не нужно.
Когда дело доходит до обработки запросов http
, вы ни от чего не отписываетесь, потому что Angular делает это за вас, почему? из-за HttpClientModule
, теперь, если вы создадите свой особенный observable
, например, subject
, там можно будет отписаться. Проверьте ответ, который я дал на этот вопрос, я не отписываюсь, в этом нет необходимости, и до сих пор использую observables
Я согласился, что await, async и promise легче понять. Однако проект, над которым я работаю, использует rxjs
и Angular. Для последовательности, я должен следовать.
"Я не отписываюсь, в этом нет необходимости" Если это работает для вас, хорошо. В нашем проекте у нас было много проблем с многократными запросами (например, третье посещение определенного компонента вызывало один и тот же внутренний запрос 3 раза) или накоплением подписок до тех пор, пока пользовательский ноутбук не стал OOM. В конце концов, вы не видите, является ли Observable автоматически отписавшимся или нет. А в обзоре кода это, вероятно, еще труднее заметить. С тех пор мы используем шаблон «ngondestroy->unsubscribe» для каждой подписки. Утечки прекратились.
Я настоятельно рекомендую использовать реактивный подход:
HTML
<ng-container *ngIf="(random$ | async) as theRandomNumber" >
<h1>{{ theRandomNumber }}</h1>
</ng-container>
<button (click)="buttonClick()">Get new Random number</button>
тс
export class AppComponent implements OnInit {
random$: Observable<number>!; // no need to initialize it
constructor(private service: Service) {}
ngOnInit(): void {}
buttonClick(): void {
this.random$ = this.service.getRandom();
}
}
Когда вы инициируете событие click
, ваше свойство публичного класса random$
будет хранить observable
из вашего сервиса, затем в вашем HTML-шаблоне, используя канал async
, вы подписываетесь на random$
, и оно будет реагировать на каждое событие click
, при этом вы Держите ваш ts-файл чище и проще
Теперь, если по какой-то причине вам нужно иметь это случайное число в вашем файле ts
, вы можете pipe
observable
и при этом сохранить этот реактивный подход:
import { tap } from 'rxjs';
export class AppComponent implements OnInit {
random$: Observable<number>!; // no need to initialize it
private random!: number;
constructor(private service: Service) {}
ngOnInit(): void {}
buttonClick(): void {
this.random$ = this.service.getRandom()
.pipe(tap((theNumber) => this.random = theNumber));
}
}
Мне нужно инициализировать random$
иначе angular не скомпилируется
Почему вы рекомендуете преобразовать
observable
вpromise
? Вы не должны использоватьpromises
в проекте Angular...