首页 > 解决方案 > BehaviorSubject 订阅未侦听值更改

问题描述

我正在创建一个有 API 服务的 Angular 应用程序。该服务使用 axios 调用 API 并根据需要更新 BehaviorSubject 的值,这是其代码。

import { Injectable } from "@angular/core";
import axios, { AxiosInstance } from 'axios';
import { BehaviorSubject, Observable, Subject } from "rxjs";
import { Post } from "../mock/post.model";
@Injectable({
    providedIn: 'root',
})
export class APIService {

    private AxiosInstance = axios.create();

    private newsList: BehaviorSubject<Post[]>;
    private startupList: BehaviorSubject<Post[]>;
    private allPosts: Post[];
    constructor() {
        this.initBS();
    }

    initBS() {
        this.newsList = new BehaviorSubject<Post[]>(null);
        this.startupList = new BehaviorSubject<Post[]>(null);
    }

    getAllPosts(): void {
        this.AxiosInstance.get(MY_API_ENDPOINT)
            .then(data => {
                const resp: Post[] = this.mapNews(data.data);
                this.allPosts = resp;
                let tempNews: Post[] = [];
                let tempStartup: Post[] = [];
                for (const post of this.allPosts) {
                    for (const cat of post.category) {
                        if (cat.toLocaleLowerCase().includes('news')) {
                            tempNews.push(post);
                        }
                        if (cat.toLocaleLowerCase().includes('startup')) {
                            tempStartup.push(post);
                        }
                    }
                }
                this.newsList.next(tempNews);
                this.startupList.next(tempStartup);
            })
            .catch(error => {
                console.error(error);
            });
    }

    getNewsList() {
        return this.newsList.asObservable();
    }

    getStartupList() {
        return this.startupList.asObservable();
    }
    
    mapNews(data: any[]): Post[] {
        return MAPPED_RESPONSE_TO_POST_ARRAY; // logic to convert axios response to Post array
    }

}

上述服务是由 home 组件的构造函数调用的。

import { Component, ViewEncapsulation } from '@angular/core';
import { Post } from '../../@core/mock/post.model';
import { APIService } from '../../@core/services/API.service';

@Component({
  selector: 'ngx-editors',
  encapsulation: ViewEncapsulation.None,
  styleUrls: ['./home.component.scss'],
  templateUrl: './home.component.html',
})
export class HomeComponent {
  newsList: Post[];
  startupList: Post[];
  constructor(private api: APIService) {
    this.newsList = [];
    this.startupList = [];
    let i = 0;
    api.getNewsList().subscribe(
      (value) => {
        console.log('onHomeNews:');
        console.log(value);
        console.log(this.startupList);
        if (value != null) {
          for (const post of value) {
            if (i < 4) {
              this.newsList.push(post);
              i++;
            }
          }
        }
      },
      (error) => {
        console.log(error);
      },
      () => {
        console.log('onComplete:');
    });

    i = 0;
    api.getStartupList().subscribe(
      (value) => {
        console.log('onHomeStartup:');
        console.log(value);
        console.log(this.startupList);
          for (const post of value) {
            if (i < 4) {
              this.startupList.push(post);
              i++;
            }
        }
      },
      (error) => {
        console.log(error);
      },
      () => {
        console.log('onComplete:');
    });
  }
}

问题是当响应来自 API 但没有检测到启动列表的值更改时,我正在获取新闻列表。我在 tempStartup 上收到响应,这意味着 API 正在发送数据。
编辑 1
我有另一个名为启动的组件,我也订阅了 getStartupList()。当我切换到该组件并返回主组件时,startupList 会更新。这是切换到启动组件之前的日志。

sub-header.component.ts:86 home
home.component.ts:40 onHomeNews:
home.component.ts:41 null
home.component.ts:42 []
home.component.ts:62 onHomeStartup:
home.component.ts:63 null
home.component.ts:64 []
home.component.ts:40 onHomeNews:
home.component.ts:41 (8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
home.component.ts:42 []

切换到启动组件并返回主组件后的日志。

sub-header.component.ts:86 home
home.component.ts:40 onHomeNews:
home.component.ts:41 null
home.component.ts:42 []
home.component.ts:62 onHomeStartup:
home.component.ts:63 null
home.component.ts:64 []
home.component.ts:40 onHomeNews:
home.component.ts:41 (8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
home.component.ts:42 []
sub-header.component.ts:86 startup
sub-header.component.ts:86 home
home.component.ts:40 onHomeNews:
home.component.ts:41 (8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
home.component.ts:42 []
home.component.ts:62 onHomeStartup:
home.component.ts:63 (5) [{…}, {…}, {…}, {…}, {…}]
home.component.ts:64 []

PS getAllPost() 从 app.component.ts 调用,以便在创建应用程序后立即调用它。

标签: angularaxiosangular-promisebehaviorsubjectsubscriber

解决方案


Angular 渲染器不知道列表何时更新push到数组。对数组的底层引用没有改变,所以它不会重新渲染模板。

相反,您可以使用扩展运算符重新分配保存数组的变量。

export class HomeComponent {
  ...

  constructor(private api: APIService) {
    this.newsList = [];
    this.startupList = [];
    let i = 0;
    api.getNewsList().subscribe(
      (value) => {
        ...
              this.newList = [...this.newsList, post];        // <-- reassign using spread operator
        ...
      },
      ...
    );

    i = 0;
    api.getStartupList().subscribe(
        ...
            this.startupList = [...this.startupList, post];   // <-- reassign using spread operator
        ...
      },
    );
  }
}

推荐阅读