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.