Angular rxjs BehavioralSubject only fires after a second call to .next is triggered

273 views Asked by At

I'm using a BehavioralSubject in my application as part of a very unique scenario, and it's not working as I was hoping it would work and I'm not entirely sure why.

The scenario:

On page load, I retrieve data from a database, containing a collection of items I need to populate onto the view via a Map<TKey, TValue>.

Once the observable completes, a call sets the .next(T) on the bevaviorsubject, however, the subscribe to this subject never triggers.

The user then interacts with the screen, a .set(T) method on the data service is called, and the behaviorsubject kicks in. When users op to fetch the data at this point, the subject I was expecting to fire first, fires off.

The structure:

The current scenario consists of the following components:

1.) planning (.ts & .html) 2.) tabs (.ts & .html) 3.) tab-content (.ts & .html)

  • planning contains a reference to which in turn, based on the tab selected, dynamically loads the component.

  • tab-content subscribes to dataservice.behavioralsubject, called assets$.

  • tab-content has a drop area that inserts new data to the database, on insert, the subscribe to assets$ fires

based on the current scenario, when /planning loads, i'm expecting assets$.subscribe(...) to fire, which does not. however, when items are droppped in tab-content, to insert data, assets$.subscribe(...) fires off as expected and if I load via a refresh button on /planning again, same as a new page load, assets$.subscribe(...) fires off at this point.

I can only surmise that the behavioralsubject in the dataservice, for some reason, has not active emits until a later point in the process, like when users insert the data after the page has loaded.

To me, this scenario is perfectly valid. I have also tried to change the change detection strategy for to OnPush, hoping that it would force a change without success.

Below the various working parts, in code, of this scenario:

<app-planning />:

// .. this code is called OnInit as well as a button click
public fetch(): void {

  // .. initialization code before calling for data from the back-end
  // .. code left out for brevity

  forkJoin([this.shiftproxy.assets(), this.shiftproxy.planningplan(this.selectedshift['id'])])
    .subscribe(([assets, plan]: [AssetModel[], ShiftPlanModel]) => {

      // .. code left out for brevity

      this.shiftplanservice.updateplan(plan, assets); // inside update is a call to .next(...)
    });

  this.showtabsettings = true;
}

<app-tab-content />:

ngOnInit(): void {
  // .. code left out for brevity

  // .. this.assetmap is just used to map the result from the Map<TKey, TValue> into something
  // .. the view needs
  this.assets$ = this.shiftplanservice.assets$.pipe(map(this.assetmap)); // subscribes to the subject
}

public itemDropped(event: CdkDragDrop<any[]>, area: string): void {
  copyArrayItem(event.previousContainer.data, event.container.data, event.previousIndex, event.currentIndex);

  // .. .set(...) also fires off .next(...), after this happens, everything works as expected
  this.shiftplanservice.set(this.tab.key, area, event.previousContainer.data[event.previousIndex]);
}

DataService:

public set(key: string, area: string, asset: AssetModel): void {

   // .. data is manipulated and added to a Map<TKey, TValue> called assets

   // .. next(...) is then called with the updated assets map
   // .. this works
   this.assets$.next(this.assets);
}

public updateplan(plan: any, assets: any): void {

  // .. based on the plan that comes back from the database
  // .. do we create a new Map<TKey, TValue> object and return that
  // .. as part of the BehaviorSubject

  // this only fires off after .set(...) has been called once, why?
  this.assets$.next(this.assets);
}

Any help, insight or alternatives would be greatly appreciated as I have no idea where else, or what else to look at.

0

There are 0 answers