Note: Please scroll down to the "Update" as the issue was boiled down to a
@Injectable
service being instanciated more than once.
I have a resolver that loads businesses:
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any {
this._logger.debug('Loading application data.');
return forkJoin([
this._userService.getMe(),
this._businessService.getAll()
]).pipe(
tap(([user, businesses]) => {
this._appState.user = user;
this._appState.businesses = businesses;
}),
finalize(() => this._logger.debug('Application data loaded.'))
);
}
There is an ApplicationState
that has two BehaviorSubject
members _business
and _businesses
:
constructor() {
this.businessChange = this._business.asObservable();
this.businessesChange = this._businesses.asObservable();
}
set business(business: BusinessModel) {
console.log(business);
this._business.next(business);
}
get business(): BusinessModel {
return this._business.getValue();
}
set businesses(value) {
this._businesses.next(value);
console.log(this.businesses);
}
get businesses(): Array<BusinessModel> {
return this._businesses.getValue();
}
As you can see, businesses
of said state gets set in the resolver. business
however depends on the route and gets set in a (lazy-loaded) module depending on that route:
ngOnInit() {
this._appState.businessesChange.subscribe(
(b) => {
console.log('businessesChange');
console.log(b);
}
);
this._subscriptions.add(
this._activatedRoute.params
.subscribe((params) => {
this._routeParams = params;
const businessId: number = parseInt(params['businessId'], 10);
const businesses = this._appState.businesses;
console.log(`businessId ${businessId}`);
console.log('available businesses');
console.log(businesses);
this._appState.business = businesses.find(b => b.id === businessId);
})
);
}
This is the relevant log output:
2019-08-28T09:11:04.989Z DEBUG [..~df7ced28.js:428] Loading application data.
ngx-logger.js:251 2019-08-28T09:11:04.992Z DEBUG [main.js:398] Fetching /me
application.state.ts:56 Set businesses:
application.state.ts:58 (3) [{…}, {…}, {…}]
ngx-logger.js:251 2019-08-28T09:11:06.781Z DEBUG Application data loaded.
businesses.component.ts:43 businessesChange
businesses.component.ts:44 []
businesses.component.ts:47 businessId 4
businesses.component.ts:48 available businesses
businesses.component.ts:49 []
application.state.ts:46 Set business:
application.state.ts:47 undefined
ngx-logger.js:251 2019-08-28T09:11:08.386Z DEBUG Place is null. No action.
As you can see, businesses
gets set and contains three elements. Then we see that Application data loaded. from the resolver. After that, we see the _activatedRoute.params
subscription stating that the route reveals a businessId
of 4
but then.. this._appState.businesses
is all of a sudden an empty list - the initial value of the BehaviorSubject
even though we printed that out a few moments earlier.
Since I am logging/debuggin the set
methods as well, I can see that this value does not get changed after being set to that list of three elements.
I have no idea what's going on here. What could possibly cause this?
What I already tried:
- Restart the server
- Use the debugger (but I printf-debug anyway)
Update
Okay I have boiled the issue down to the ApplicationState
being instanciated more than once:
By giving the ApplicationState
a simple ID (timestamp):
constructor() {
this.businessChange = this._business.asObservable();
this.businessesChange = this._businesses.asObservable();
this.timestamp = moment().format('MM.SS SSS');
console.log(`${this.timestamp} ApplicationState created`);
}
and changing the log-output in the set
and get
methods, I get this:
08.09 095 ApplicationState created
ngx-logger.js:251 2019-08-28T09:51:16.098Z DEBUG Loading application data.
ngx-logger.js:251 2019-08-28T09:51:16.099Z DEBUG [main.js:398] Fetching /me
:4200/#/businesses/4/calendar:1 This site does not have a valid SSL certificate! Without SSL, your site's and visitors' data is vulnerable to theft and tampering. Get a valid SSL certificate before releasing your website to the public.
application.state.ts:60 08.09 095 Set businesses:
application.state.ts:62 (3) [{…}, {…}, {…}]
application.state.ts:38 08.14 145 ApplicationState created
ngx-logger.js:251 2019-08-28T09:51:16.161Z DEBUG Application data loaded.
businesses.component.ts:43 businessesChange
businesses.component.ts:44 []
businesses.component.ts:54 businessId 4
businesses.component.ts:55 available businesses
businesses.component.ts:56 []
application.state.ts:50 08.14 145 Set business:
application.state.ts:51 undefined
ngx-logger.js:251 2019-08-28T09:51:16.178Z DEBUG Place is null. No action.
calendar.component.ts:105
So there is a 08.09 095
(A) and a 08.14 145
(B) instance of ApplicationState
.
A gets the businesses set but further B is used apparently.
I checked, ApplicationState
gets imported only once in my ApplicationModule
:
providers: [
// Services
ApplicationState,
ApiServices,
ApplicationResolver,
]
So why is this happening? Shouldn't this be Singleton? Is it because this goes over a lazy-loaded module?
But most importantly: Why did this work for the last few weeks?
I also added providedIn
by now:
@Injectable({providedIn: 'root'})
export class ApplicationState {
but it still behaves the same.