【问题标题】:How to use BehaviorSubject value as an argument for a method?如何使用 BehaviorSubject 值作为方法的参数?
【发布时间】:2019-09-12 08:59:32
【问题描述】:

我正在使用 BehaviorSubject 来保存与其他组件共享的变量(用户名),它在我的模板上显示用户名的效果很好。但是,我希望能够在服务中使用我的 BehaviorSubject 的值,然后将该值(用户名)用作如下方法的参数:myMethod(username){ do stuff }。

问题似乎在于调用该方法时 BehaviorSubject 值尚未准备好。我已经尝试在构造函数和 ngOnInIt 中调用该方法,如果我将它记录到控制台,它首先显示它是未定义的,然后最终它使用正确的(用户名)值记录。是否可以使用 BehaviorSubject 获取作为参数传递给方法的值?

在我的示例中,我有一个 auth.service,其中包含我的 BehaviorSubject(不显示登录/身份验证内容,因为它与此示例无关)。然后在我的 newform.component.ts 中订阅了那个 BehaviorSubject。我可以在新表单模板上显示值,但这不是我想要做的。 我正在努力的是订阅 newform.component 中的 BehaviorSubject 值并将其传递给一个调用服务的方法,该服务应该根据用户名返回一个位置数组。我需要该位置数组来填充新表单模板上的输入,这就是我尝试在 ngOnInIt 上调用它的原因......所以我可以在用户开始填写表单之前获取输入的位置。

下面是一些代码:

auth.service.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  private accountInfoSource = new BehaviorSubject<any>({});
  accountInfoCurrent = this.accountInfoSource.asObservable();

  constructor() { }

  accountInfoChange(accountName: any) {
    this.accountInfoSource.next(accountName)
  }

}

newform.component.ts

import { Component, OnInit } from '@angular/core';
import { Locationslist } from '../services/locationslist.service';
import { AuthService } from '../services/auth.service';


@Component({
  selector: 'app-new-charge',
  templateUrl: './new-charge.component.html',
  styleUrls: ['./new-charge.component.css']
})
export class NewChargeComponent implements OnInit {

  accountName: string;
  locations;

  constructor(protected authService: AuthService, private locations: Locationslist) { }

  ngOnInit() {
    this.getLocations();
  }

  getLocations() {
    this.authService.accountInfoCurrent.subscribe(accountInfo => {
      this.accountName = accountInfo.AccountName;
    } );


    this.locations.getUserLocations(this.accountName)  // <===  this.accountName is undefined
      .subscribe(foundset => {
        this.locations = foundset;
       });

  }


}

locationslist.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class Valuelists {

  constructor(private http: HttpClient, protected authService: AuthService) { }

  getUserLocations(accountname) {
    let token = localStorage.getItem('token');
    let body = '{\"query\": [ { \"AccountName\" : \"=' + accountname + '\"} ] }';
    const findLocations = 'https://my.api';
    let auth = 'Bearer ' + token;

    return this.http.post(findLocations, body, {
      headers: new HttpHeaders()
        .set('Content-Type', 'application/json')
        .set('Access-Control-Allow-Origin', '*')
        .set('Authorization', auth)
    })
  }
}

如果我将“帐户名”硬编码到 getUserLocations(accountname) 的参数中,我知道位置列表服务可以工作。事实上,locationservice 最终会使用 BehaviorSubject 值,但在控制台中我首先看到一个错误,http 响应为 500,然后我最终获得了位置信息。再一次,似乎 BehaviorSubject 变量出现得很晚。有没有办法将 BehaviorSubject 值作为参数传递给方法,还是有更好的方法来实现我的目标?

【问题讨论】:

  • 感谢大家的意见和帮助。我最终使用路由参数将数据从一个组件发送到下一个组件。将“参数”传递给另一个组件的“正确”方式更直接,更有可能是“正确”的方式(名称路由参数!)。

标签: angular typescript behaviorsubject


【解决方案1】:

当然,您只需要在函数本身中使用响应式方法。

name$: BehaviorSubject<string> = new BehaviorSubject("");

function doSomething() {
  this.name$.subscribe(console.log); // Here you have that name
  // You can filter it like (.pipe(filter(name => !!name)) is you don't want undefined value
}

// Your location function would look like this:
function getLocations() {
  this.name$.pipe(switchMap(name => {  // Every time name subject changes, switch to another getUserLocations
    return this.locations.getUserLocations(name)
  }))
  .subscribe(foundset => {
    this.locations = foundset;
   });
}

像上面的函数一样为getLocations函数删除未定义的值。

【讨论】:

    【解决方案2】:

    在这种情况下,您应该查看 Resolve Guard。这样做的目的是在组件加载之前预取一些数据。从您的问题来看,您似乎需要先在组件中使用用户名,然后再执行其余功能。

    创建解析保护的基本步骤是

    1. 创建一个获取用户名的服务

    2. 创建一个解析守卫,它实现resolve 方法以通过调用您的服务返回一个可观察对象

    3. 在该组件的路由配置中添加解析保护的引用

    4. 在你的组件中,订阅组件数据参数并获取用户名

    请注意,路由配置中的组件可以有多个解析保护。

    https://www.concretepage.com/angular-2/angular-resolve-guard-example 上有一个很好的例子

    【讨论】:

      【解决方案3】:

      你需要连接这两个方法,并让第二个等待第一个完成。

      您可以使用 map 运算符来做到这一点,只需将第二个函数放在 map 函数中即可。

       getLocations() {
          this.authService.accountInfoCurrent.pipe(map(() => {
                      this.locations.getUserLocations(this.accountName)  
                            .subscribe(foundset => {
                              this.locations = foundset;
                            });
          })).subscribe(accountInfo => {
            this.accountName = accountInfo.AccountName;
          } );
      }
      

      【讨论】:

      • 在订阅中嵌套订阅实际上是一种不好的做法。最好使用 switchMap() 操作符链接它。
      【解决方案4】:

      感谢大家的意见和帮助。我最终使用路由参数将数据从一个组件发送到下一个组件。将“参数”传递给另一个组件的“正确”方式更直接,更有可能是“正确”的方式(因此命名为路由参数!)。它也不是异步的,因此您不必“等待” BehaviorSubject observable。可以直接加载OnInIt。

      希望有帮助!

      【讨论】:

        【解决方案5】:

        使用 angular 12 可以添加 null 或 undefined!:

        subjectUser:BehaviorSubject<User> = new BehaviorSubject<User>(null!);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-18
          • 1970-01-01
          • 2021-06-29
          相关资源
          最近更新 更多