Я испытываю любопытное поведение с BehaviorSubject в моем угловом (7) приложении.
Я создал службу, которая потребляет некоторые остальные вызовы. Чтобы представить, что происходит в коде, вот вы:
export class MyService {
private dataFromServer = new BehaviorSubject<IDataFromServer[]>(null);
constructor(private http: HttpClient) { }
getAll(): Observable<IDataFromServer[]> {
this.http.get<IDataFromServer[]>('/api/rest')
.pipe(
tap(data => this.dataFromServer.next(data))
);
}
return this.dataFromServer.asObservable();
}
Все идет нормально. Когда мне нужно использовать эту услугу, я подписываюсь на метод getAll(), например
this.myService.getAll().subscribe(console.info);
и вывести данные на консоль.
Теперь мне нужно добавить данные из интерфейса в остальные API.
export class MyService {
private dataFromServer = new BehaviorSubject<IDataFromServer[]>(null);
// snip...
add(item: IDataFromServer): Observable<any> {
return this.http.post<IDataFromServer>('/api/manage', item).pipe(
tap(data => {
let internal = this.dataFromServer.getValue();
if (!internal) {
internal = new Array<IDataFromServer>();
}
internal.push(material);
this.dataFromServer.next(internal);
})
);
}
}
Вот тут и начинаются проблемы. Подписка выше печатается в первый раз, но не получает новые данные, инициированные функцией next().
Что меня озадачивает, так это то, что если я нажимаю обновить в браузере, не меняя ни одной строки в коде, подписка возвращается к жизни каждый раз, когда я нажимаю next().
Я явно что-то делаю не так, но я не понимаю, почему и где.
Спасибо за любую вашу помощь.
В вашем последнем фрагменте тот факт, что ничего не подписалось на BehaviorSubject, кажется подозрительным.
из вашего кода видно, что вы подписываетесь на метод add, а не на dataFromServer, поскольку он частный, и вы не сможете подписаться на него изнутри..
это так, getAll, кажется, возвращает тему, однако фигурные скобки должны быть неуместны в этой функции.
Не видя этого, я бы предположил, что результат add не подписан. Поэтому tap никогда не вызывается.
См.: stackoverflow.com/questions/52188795/…





Проблема в том, что вы звоните this.http.get<IDataFromServer[]>('/api/rest') внутри getAll, но никто никогда не подписывается на него.
Первый журнал, который вы упомянули, должен быть null, потому что вы инициализируете свой BehaviorSubject нулем.
Что вам нужно, так это просто вернуть http.get из getAll, чтобы любой, кто вызывает этот метод и подписывается на него, запускал HTTP-вызов. Кроме того, tap позаботится о том, чтобы ваши данные в BehaviorSubject были обновлены.
getAll(): Observable<IDataFromServer[]> {
return
this.http.get<IDataFromServer[]>('/api/rest')
.pipe(
tap(data => this.dataFromServer.next(data))
);
}
Спасибо за Ваш ответ. Если я добавлю возврат и удалю возврат this.dataFromServer.asObservable(); как вы предложили, работает в первый раз, но последующие обновления по-прежнему не работают.
Хотя вы не включили часть, где вы вызываете функцию add, я почти уверен, что вы не подписываетесь на ее результат.
У вас может быть строка кода, которая говорит что-то вроде
myService.add(myItem);
Теперь это вернет Observable. Однако обратите внимание, что если никто не подпишется на этот Observable, подключенные каналы не будут активированы.
Следовательно, у вас есть два решения.
Первое: не используйте tap, а подписывайтесь. Я почти уверен, что это то, что вы хотите. Это выглядит так:
add(item: IDataFromServer): void {
this.http.post<IDataFromServer>('/api/manage', item)
.subscribe(data => {
let internal = this.dataFromServer.getValue();
if (!internal) {
internal = new Array<IDataFromServer>();
}
internal.push(material);
this.dataFromServer.next(internal);
}
);
}
Второе: Подпишитесь на результат add. Что-то вроде этого:
myService.add(myItem).subscribe();
Проблема возникает из-за распространенного неправильного понимания Observables. Может этот пост поможет.
Если я подпишусь на add(), я получу ошибку, которая не является наблюдаемой.
Вы можете сделать либо первое, либо второе решение. Если вы хотите подписаться на него (решение 2), то add необходимо вернуть наблюдаемое, как показано в исходном фрагменте.
Вы правы, и вместе с ответом @bracco23 прояснили мне мою ошибку. Мое единственное сожаление, что я не могу отметить ваш комментарий, решение среди ответов
Вместе с отличными ответами на вопрос, почему ваше решение не работает, я добавлю свое мнение о том, что не так с вашим подходом.
Из того, что я вижу, вы пытаетесь предоставить единственную наблюдаемую из вашей службы, которая будет использоваться при каждом вызове REST, позволяя представлениям отделять и повторно использовать логику визуализации данных от входа в систему обновления данных (а именно, им просто нужно будет отображать данные один раз и каждый раз, когда они обновляют данные, будет вызываться одна и та же логика).
Проблема в том, что httpClient Observables ленивы, фактически отправляют запросы, только если кто-то подписывается.
Я сделал то же самое, и обычно я скрываю http-вызовы в своем сервисе и подписываюсь внутри. Заимствуя ваши классы, это будет выглядеть так:
export class MyService {
private dataFromServer = new BehaviorSubject<IDataFromServer[]>(null);
constructor(private http: HttpClient) { }
getAll(): Observable<IDataFromServer[]> {
this.http.get<IDataFromServer[]>('/api/rest')
.subscribe(data => this.dataFromServer.next(data));
return this.dataFromServer.asObservable();
}
add(item: IDataFromServer): Observable<any> {
return this.http.post<IDataFromServer>('/api/manage', item)
.subscribe(data => this.dataFromServer.next(data));
}
}
Я также предполагаю, что ваша конечная точка следует принципам REST, поэтому путь /api/manage возвращает в качестве ответа полный список, обновленный новыми добавленными данными, точно так же, как вызов /api/rest/ возвращается после вызова POST.
Это помогло. Спасибо и спасибо @ggradnig за то, что указал мне на правильный способ думать о моей ошибке.
Где называется
add? пожалуйста, включите это в свои фрагменты