Rxjs странная проблема с поведением субъекта - PullRequest
1 голос
/ 03 мая 2019

Я испытываю странное поведение с BehaviorSubject в моем приложении angular (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.log);

, и печатаю данные в консоль.

Теперь мне нужно добавить данные из внешнего интерфейса востальные 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 ().

Меня удивляет то, что если я нажму кнопку «Обновить» в браузере, не меняя строку кода, подпискавозвращаться к жизни каждый раз, когда я нажимаю следующий ().

Я, очевидно, что-то делаю не так, но я не понимаю, почему и где.

Спасибо за любую вашу помощь.

Ответы [ 3 ]

3 голосов
/ 03 мая 2019

Проблема в том, что вы звоните 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))
        );
 }
1 голос
/ 03 мая 2019

Вместе с отличными ответами о том, почему ваше решение не работает, я добавлю свое мнение о том, что не так с вашим подходом.

Из того, что я вижу, вы пытаетесь выявить одну наблюдаемуюот вашего сервиса, который будет использоваться при каждом вызове 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.

1 голос
/ 03 мая 2019

Хотя вы не включили часть, где вы вызываете функцию 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();

Проблема возникает из-за распространенного недопонимания по поводу наблюдаемых. Может быть, этот пост поможет .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...