JSkyS
JSkyS

Reputation: 443

BehaviorSubject value is returned when not needed. Sometimes resets the value that is actually needed

I have a button, that is set up to update the parameters of a component. This works when I hit the button, that updates the parameters. Sometimes when I'm refreshing the page or navigating to it trough a URL from an external page, the update logic doesn't work.

I know this is because the behaviorSubject is hard coded to (2019,1,1,'Stores') as shown below. Why the author put that in I'm not sure. All I know is that the object with (2019,1,1,'Stores') is not needed on refresh. I have a method getSLGCurrentWeek() also shown below, that is supposed to update the (2019,1,1,'Stores') to the current year, quarter, and week. Which is now 2020, 1,4.

Why is that hard coded value showing up when I am trying to reset it? Or better yet what would be the best way to get the correct values from the http request getSLGCurrentWeek() so that I can use those values in the getSLGData(params) method?

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);
  }

}

Here is my 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),
    )

  }

Button for update

  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")
  }

In conclusion. How to I run this.slgService.getSLGCurrentWeek() to get the params to put into this.slgService.getSLGData(params) without the influence of (2019,1,1,'Stores') from the BehaviorSubject?

Thank you for your assistance.

Upvotes: 0

Views: 1425

Answers (3)

millenion
millenion

Reputation: 1877

If I may suggest a little track for your problem here => You should be really careful about your subscriptions: Do not forget to "unsubscribe" all of them by implementing ngDestroy. Everytime you'll load your component, it will subscribe again and again.

If you wanna have an idea: Go to your console, and check memory used, or at least check logs, you should see it going to the roof after a while.

I hope this helped you a bit.

Upvotes: 1

those are very interesting pieces of code. If I were in your shoes, I would try to do something like the following snippet. If you can reproduce the issue in stackblitz or enter link description here, I can provide you with full example.

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)
  })
}

So what I did differently, Instead of depending on the BehaviorSubject inside of the service, I used a more functional/reactive approach, where I'm getting all the required data with the help of one single subscription, trying to remove all dependencies on data that is external to the request stream.

You can see what I mean, by looking at the way that I'm gathering the slgParams data, instead of depending on the form with the patched values, I'm directly getting the data from the "source" (the data object received in the stream).

A piece of friendly advice, If you are going to work on this project for a longer period of time, I will strongly recommend you invest some time in learning the principals of reactive programming, Angular, rxjs, ngrx.

Upvotes: 1

Andrei Gătej
Andrei Gătej

Reputation: 11934

Why is that hard coded value showing up when I am trying to reset it?

This happens because you are using BehaviorSubject. That initial value that you provide to the constructor is what you initially get when you do

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

What's interesting about this kind of subject is that it will cache the last emitted value. As a result, new subscribers will receive that value.

Here's how I think you can skip that first emitted value, by using the skip operator.

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

Upvotes: 1

Related Questions