【问题标题】:Rxjs: Observables and Subjects orderingRxjs: Observables 和 Subjects 排序
【发布时间】:2020-05-10 08:50:15
【问题描述】:

我有一个“用户”组件及其相关服务:userService。我想将组件表单用于新建和编辑。

在订阅服务主题时,用户组件与其他几个组件(国家和州)及其服务相关。

export class CountrystateService {

  private countries: Country[] = [];
  private countriesSubject = new Subject<Country[]>();
  countriesSubject$ = this.countriesSubject.asObservable();

  private allStates: State[];
  private states: State[];
  private statesSubject = new Subject<State[]>();
  statesSubject$ = this.statesSubject.asObservable();

  constructor(private apiService: ApiService) { }

  getCountriesAllStatesFromRest() {
    forkJoin({
        countries: this.apiService.getEntriesFromRest('countries').pipe(
            tap((countries: Country[])=> {this.countries=countries;})),
        states: this.apiService.getEntriesFromRest('countries').pipe(
            tap((states: State[])=> {this.allStates=states;}))
        }).subscribe(
            (results) => {
                this.countriesSubject.next(this.countries.slice());
            },
            (error) => {
                this.countriesSubject.next(error);
                return throwError(error);
            },
            () =>{}
        )
    }

    filterStatesForCountry(countryId?) {
    if (countryId) {
        this.states=this.allStates.filter(item => +item.country.split('/').pop() === countryId);
        this.statesSubject.next(this.states);
    }

  }

用户组件 ngOnInit:

  ngOnInit() {

    this.stateSubscription = this.countrystateService.statesSubject$.subscribe(
      (states: State[]) => {
        if (states) {
            this.states = states;       
        this.FooFunction();
        }
      },
      (error) => {..}
    );

    this.countrySubscription = this.countrystateService.countriesSubject$.subscribe(
      (countries: Country[]) => {
        if (countries) {
            this.countries = countries;         
        }
      },
      (error) => {...}
    );

    this.userSubscription = this.userService.userSubject$.subscribe(
      (user: User=> {
        if (user) {
            this.user = user;
        }
      },
      (error) => {...}
    );

在编辑上下文(参数中的用户 ID)中,我需要订购请求: - 获取用户 - 获取有关 user.country.id 的州列表

然后我尝试了这样的事情:

this.userIdSubscription = this.route.paramMap.subscribe(
    (params: ParamMap) => {
        const userId = +params.get('id');
        if (userId) {
            ...
            this.userService.getUserFromRest(userId);
            this.countrystateService.getCountriesAllStatesFromRest();
            forkJoin({
                user: this.userService.userSubject$,
                states: this.countrystateService.statesSubject$,
            }).pipe(
                tap(results => console.log('FORKJOIN TRIGGERED')),
                tap(results => {
                    ...
                    this.countrystateService.filterStatesForCountry(results.user.country.id);
                }),
            );
        }
        else { this.countrystateService.getCountriesAllStatesFromRest(); }
    }
);

但它无法正常工作... 谢谢你的帮助,

【问题讨论】:

  • 请分享userSubject$statesSubject$的代码

标签: angular rxjs subject-observer


【解决方案1】:

更新

经过调查,我们发现 Subjects$ 没有完成,因此 forkJoin 不适合,解决方案是改用 combineLatest().pipe(first())

原创

正确的做法是单一订阅和构建管道,尝试使用下一种方法遵循 rx 风格。

this.userIdSubscription = this.route.paramMap.pipe(
  map((params: ParamMap) => +params.get('id')),
  filter(userId => !!userId),
  tap(userId => {
    // ...
    this.userService.getUserFromRest(userId);
    this.countrystateService.getCountriesAllStatesFromRest();
  }),
  switchMap(userId => forkJoin({
    user: this.userService.userSubject$,
    states: this.countrystateService.statesSubject$,
  })),
  tap(results => {
    this.countrystateService.filterStatesForCountry(results.user.country.id);
  }),
).subscribe();

现在 IMO 更具可读性。


你好像忘记订阅了。

            forkJoin({
                user: this.userService.userSubject$,
                states: this.countrystateService.statesSubject$,
            }).pipe(
                tap(results => console.log('FORKJOIN TRIGGERED')),
                tap(results => {
                    ...
                    this.countrystateService.filterStatesForCountry(results.user.country.id);
                }),
            ).subscribe(); // <- this place.

如果不是答案,那么看起来服务还没有完成,所以你不能使用 forkJoin,它希望所有的 observables 都完成。

尝试使用 combineLatest,它会在第一次发出后发出。

            combineLatest([ // <- this change.
                user: this.userService.userSubject$,
                states: this.countrystateService.statesSubject$,
            ]).pipe(
                first(), // <- we need only the first result?
                tap(results => console.log('FORKJOIN TRIGGERED')),
                tap(([user]) => {
                    ...
                    this.countrystateService.filterStatesForCountry(user.country.id);
                }),
            ).subscribe(); // <- this change too.

【讨论】:

  • 感谢您的帮助。我实施了您的干净代码提案。我将 console.log 添加到我的 this.xxxSubscription = this.xxxeService.xxxsSubject$.subscribe(..) 函数中,并在 Firefox 中检查了请求列表。现在发出所有请求并终止但最后一次点击 (tap(result => { this.countrystateService.filterStatesForCountry(result.user.country.id); }), ) 没有被触发(在里面添加了一个 console.log 来检查)
  • 如果他们被终止 - 那么这意味着订阅被终止,你是否在任何地方调用 unsubscribe ?
  • 是的 - 听起来不像是取消的原因,您是否希望在更改参数时加载一次或每次?
  • 随意创建一个聊天,我们可以在那里讨论,问题看起来并不复杂,我们只需要找出问题所在:)
  • 你是怎么聊天的?
【解决方案2】:

使用来自ActivatedRouteparams 而不是来自RouterparamMap

constructor(private aroute:Activated, ...) { ... }

this.userIdSubscription = this.aroute.params.subscribe(
    (params) => {
      const userId = params.id;
      ...

}

希望对你有帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    • 2020-02-10
    • 2016-07-13
    相关资源
    最近更新 更多