【问题标题】:BehaviorSubject value is returned when not needed. Sometimes resets the value that is actually needed不需要时返回 BehaviorSubject 值。有时会重置实际需要的值
【发布时间】:2019-11-25 20:12:08
【问题描述】:

我有一个按钮,用于更新组件的参数。这在我点击按钮时有效,更新参数。有时,当我刷新页面或通过外部页面的 URL 导航到它时,更新逻辑不起作用。

我知道这是因为 behaviorSubject 被硬编码为 (2019,1,1,'Stores'),如下所示。为什么作者把它放进去我不确定。我所知道的是,刷新时不需要带有(2019,1,1,'Stores') 的对象。我有一个方法getSLGCurrentWeek() 也如下所示,它应该将(2019,1,1,'Stores') 更新为当前年份、季度和周。现在是 2020 年,1,4。

为什么当我尝试重置它时会显示该硬编码值?或者更好的是从http请求getSLGCurrentWeek()获取正确值的最佳方法是什么,以便我可以在getSLGData(params)方法中使用这些值?

export class SlgService {

  private slgParamsSource: Subject<SLGReportParams> = new BehaviorSubject<SLGReportParams>(new SLGReportParams(2019, 1, 1, 'Stores'));
  slgParams = this.slgParamsSource.asObservable();

  constructor(private http: HttpClient, @Inject('BASE_URL') private baseUrl: string) { }

  getSLGData(params: SLGReportParams) {
    var slgParams = '?year=' + params.Year + '&quarter=' + params.Quarter + '&week=' + params.Week + '&dept=' + params.Dept;
    return this.http.get(this.baseUrl + 'api/slg/slg' + slgParams);
  }

  getSLGCurrentWeek() {
    return this.http.get(this.baseUrl + 'api/slg/SlgCurrentWeek');
  }

  getSLGWeeks() {
    return this.http.get<any[]>(this.baseUrl + 'api/slg/slgGetAllWeeks');
  }

  updateSLGParams(params: SLGReportParams) {
    this.slgParamsSource.next(params);
  }

}

这是我的 ngOnInit()

  ngOnInit() {

    console.log("here")

    this.slgForm = new FormGroup({
      year: new FormControl(),
      quarter: new FormControl(),
      week: new FormControl(),
      dept: new FormControl()
    })

    console.log(this.slgForm)


    //subscribe to slg Service params observable
    this.slgService.slgParams.subscribe(data => {

      this.slgParams = data;
      this.getData(this.slgParams);
    })


    this.slgService.getSLGCurrentWeek().subscribe((data: any) => {
      //set SlgForm with current week values.
      this.slgForm.patchValue({
        year: data.year,
        week: data.week,
        quarter: data.quarter
      })

      this.slgParams = new SLGReportParams(this.slgForm.value.year, this.slgForm.value.quarter, this.slgForm.value.week, this.slgForm.value.dept);
      this.slgService.updateSLGParams(this.slgParams);

      //this.populateSLGTotals();
      //this.operations(data);
      this.getData(this.slgParams)
      //update service with params // Submit has been taken out here
    },
      error => console.log(error),
    )

  }

更新按钮

  submit() {
    this.slgParams = new SLGReportParams(this.slgForm.value.year, this.slgForm.value.quarter, this.slgForm.value.week, this.slgForm.value.dept);
    this.slgService.updateSLGParams(this.slgParams);
    console.log("I have submitted")
  }

总之。如何在不受 BehaviorSubject (2019,1,1,'Stores') 影响的情况下运行 this.slgService.getSLGCurrentWeek() 以将参数放入 this.slgService.getSLGData(params)

感谢您的帮助。

【问题讨论】:

  • 感谢您的所有回答和 cmets。我正在努力阅读所有这些以更正我的代码。我会在阅读完所有这些后通知您。谢谢你,很快就会回来。再次感谢!
  • 感谢您的意见。将我的 BehaviorSubject 更改为私有 slgParamsSource:ReplaySubject = new ReplaySubject(null);现在它没有被重置。我尝试对具有空值的 BehaviorSubject 执行相同的操作,但没有成功。你知道为什么这会奏效吗?我正在阅读很多关于 replaySubject 和 behaviorSubject 之间的区别,但奇怪的是返回多个回调的东西会解决这个问题。我很困惑为什么它现在有效。我也问过下面的其他人。有人解开这个谜团吗?
  • 还有什么理由让 ReplaySubject = new ReplaySubject(null) 可以在调试模式而不是生产模式下工作?我刚刚发布后出现错误,不得不恢复。

标签: javascript angular rxjs observable behaviorsubject


【解决方案1】:

如果我可以在这里为您的问题提出一些建议 => 您应该非常小心您的订阅:不要忘记通过实现 ngDestroy 来“取消订阅”所有订阅。每次加载组件时,它都会一次又一次地订阅。

如果你想知道:去你的控制台,检查使用的内存,或者至少检查一下日志,你应该会看到它在一段时间后达到顶峰。

希望对你有所帮助。

【讨论】:

  • 感谢您的意见。将我的 BehaviorSubject 更改为私有 slgParamsSource:ReplaySubject = new ReplaySubject(null);现在它没有被重置。我尝试对具有空值的 BehaviorSubject 执行相同的操作,但没有成功。你知道为什么这会奏效吗?我正在阅读很多关于 replaySubject 和 behaviorSubject 之间的区别,但奇怪的是返回多个回调的东西会解决这个问题。我很困惑为什么它现在有效。
【解决方案2】:

为什么在我尝试重置时会显示硬编码值?

这是因为您使用的是BehaviorSubject。您提供给 构造函数 的初始值是您最初得到的值

this.slgService.slgParams.subscribe(data => {
 this.slgParams = data;
 this.getData(this.slgParams);
})

这种主题的有趣之处在于它会缓存 最后发出的值。因此,新订阅者将收到该值。

我认为您可以使用skip operator 跳过第一个发出的值。

this.slgService.slgParams
 .pipe(skip(1))
 .subscribe(data => {
   this.slgParams = data;
   this.getData(this.slgParams);
})

【讨论】:

  • 感谢您的意见。将我的 BehaviorSubject 更改为私有 slgParamsSource:ReplaySubject = new ReplaySubject(null);现在它没有被重置。我尝试对具有空值的 BehaviorSubject 执行相同的操作,但没有成功。你知道为什么这会奏效吗?我正在阅读很多关于 replaySubject 和 behaviorSubject 之间的区别,但奇怪的是返回多个回调的东西会解决这个问题。我很困惑为什么它现在有效。你能解释一下吗?我问过其他人同样的事情。
【解决方案3】:

这些都是非常有趣的代码。如果我在你的鞋子里,我会尝试做类似下面的 sn-p 的事情。如果您可以在stackblitzenter link description here 中重现该问题,我可以为您提供完整的示例。

ngOnInit() {
  this.slgForm = new FormGroup({
    year: new FormControl(),
    quarter: new FormControl(),
    week: new FormControl(),
    dept: new FormControl()
  })

  this.slgService.getSLGCurrentWeek().pipe(
    map(data => {
      this.slgForm.patchValue({
        year: data.year,
        week: data.week,
        quarter: data.quarter
      })
      // Those are your slg params, that you will get as value from the stream
      return new SLGReportParams(data.year, data.quarter, data.week, this.slgForm.value.dept)
    }),
    switchMap(slgParams => {
      // This will return new stream, with the response of the get data http request
      return this.slgService.getSLGData(slgParams);
    })
  ).subscribe(resp => {
    // Here you will have access to the data recieved from the getData function
    console.log(resp)
  })
}

所以我做了不同的事情,而不是依赖于服务内部的BehaviorSubject,我使用了一种更具功能性/反应性的方法,我在一个订阅的帮助下获取所有必需的数据,试图移除对请求流外部数据的所有依赖。

您可以通过查看我收集slgParams 数据的方式来了解我的意思,而不是依赖于带有修补值的表单,我直接从“源”获取数据(流中接收到的data 对象)。

一个友好的建议,如果你打算在这个项目上工作更长的时间,我强烈建议你花一些时间学习反应式编程的原理,Angular,rxjsngrx .

【讨论】:

  • 感谢您的意见。将我的 BehaviorSubject 更改为私有 slgParamsSource:ReplaySubject = new ReplaySubject(null);现在它没有被重置。我尝试对具有空值的 BehaviorSubject 执行相同的操作,但没有成功。你知道为什么这会奏效吗?我正在阅读很多关于 replaySubject 和 behaviorSubject 之间的区别,但奇怪的是返回多个回调的东西会解决这个问题。我很困惑为什么它现在有效。
猜你喜欢
  • 1970-01-01
  • 2017-11-02
  • 2013-05-18
  • 2019-08-03
  • 1970-01-01
  • 2016-06-16
  • 2012-07-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多