Reputation: 507
I have got this class that acts as a mediator:
export class ProgressBarService {
// Observable string sources
private progressAnnouncement = new Subject<number>();
// Observable string streams
progressAnnounced$ = this.progressAnnouncement.asObservable();
constructor() { }
AdministerProgress(progress: number) {
this.progressAnnouncement.next(progress);
}
}
Component A sends a message like this in ngOnInit:
this.progressBarService.AdministerProgress(100);
But it never gets to the navigational component:
constructor(private breakpointObserver: BreakpointObserver,
private requestBuilder: RequestBuilder,
private cdref: ChangeDetectorRef,
private router: Router,
private progressBarService: ProgressBarService ) {
}
ngOnInit(): void {
}
ngAfterContentChecked() {
this.progressBarService.progressAnnounced$.subscribe(announcement => {
console.log(announcement);
this.progressBarValue = announcement;
this.cdref.detectChanges();
});
}
I get no message for the console.log. Why is that? UPDATE I followed this explanation Behavior subject
import { Injectable } from '@angular/core';
import { BehaviorSubject, Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ProgressBarService {
// Observable string sources
public progressAnnouncement$ = new BehaviorSubject(0);
constructor() { }
AdministerProgress(progress: number) {
console.info('inside func',progress);
this.progressAnnouncement$.next(progress);
}
}
The navigation header of the page which loads first subscribes:
ngAfterContentChecked() {
this.progressBarService.progressAnnouncement$.subscribe(announcement => {
console.info('Announcement', announcement);
this.progressBarValue = announcement;
this.cdref.detectChanges();
});
}
Then another component that loads much later emits values by using the AdministerProgress function, as such:
this.progressBarService.AdministerProgress(100);
The header component never receives the 100 value. All it gets are many 0 values one after the other.
UPDATE:
Here is the shared class:
import { Injectable } from '@angular/core';
import { BehaviorSubject, Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ProgressBarService {
public progressAnnouncement$ = new BehaviorSubject(0);
constructor() { }
AdministerProgress(progress: number) {
this.progressAnnouncement$.next(progress);
}
}
Here is the singleton module that I created, as was explained in this link:
https://angular.io/guide/singleton-services#providing-a-singleton-service
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProgressBarService } from 'app/Services/Classes/ProgressBarServices/ProgressBarService';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class SingletonModule {
constructor(@Optional() @SkipSelf() parentModule?: SingletonModule) {
if (parentModule) {
throw new Error(
'SingletonModule is already loaded. Import it in the AppModule only');
}
}
static forRoot(): ModuleWithProviders<SingletonModule> {
return {
ngModule: SingletonModule,
providers: [
ProgressBarService
]
};
}
}
This is the section of my app.module where I import that module:
imports: [
CommonModule,
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MatsharedModule,
HttpClientModule,
SharedModule,
PrimeNGModule,
LayoutModule,
SingletonModule.forRoot(),
Here is my main AppRoutingModule config file:
import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';
import { DashboardComponent } from './Dashboard/dashboard/dashboard.component';
import { PageNotFoundComponent } from './Errors/page-not-found/page-not-found.component';
import { AuthGuard } from './Services/Guards/auth.guard';
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard]
},
{
path: 'authentication',
loadChildren: () => import('./authentication/authentication.module').then(m => m.AuthenticationModule)
},
{
path: 'accounting',
loadChildren: () => import('./accounting/accounting.module').then(m => m.AccountingModule),
canActivate: [AuthGuard]
},
{
path: 'users',
loadChildren: () => import('./users/users.module').then(m => m.UsersModule),
canActivate: [AuthGuard]
},
{
path: 'game-management',
loadChildren: () => import('./game-management/game-management.module').then(m => m.GameManagementModule),
canActivate: [AuthGuard]
},
{
path: 'operator',
loadChildren: () => import('./operator-management/operator-management.module').then(m => m.OperatorManagementModule),
canActivate: [AuthGuard]
},
{
path: 'technical',
loadChildren: () => import('./technical/technical.module').then(m => m.TechnicalModule),
canActivate: [AuthGuard]
},
{
path: '404',
component: PageNotFoundComponent
},
{
path: '',
redirectTo: 'dashboard',
pathMatch: 'full'
},
{
path: '**',
redirectTo: '404',
pathMatch: 'full'
}
];
@NgModule({
imports: [RouterModule.forRoot(routes,
{
// enableTracing: true,// <-- debugging purposes only
preloadingStrategy: PreloadAllModules
}
)],
exports: [RouterModule]
})
export class AppRoutingModule { }
The other module dont have a trace of the service provided or the singleton module imported.
I believe that I miss some configuration.
Upvotes: 0
Views: 46
Reputation: 2987
It's likely that the navigation component
subscribe to the Observable after component A
fire the data, which mean it miss the data.
The solution is to change progressAnnouncement
from Subject
to BehaviorSubject
or ReplaySubject
. Each of those two have slighly different use but the main different from Subject
is they store the data inside and new subscribers always have the latest data (BehaviorSubject
) or all the data emitted (ReplaySubject
).
Update :
In case of layzy-loading, there change that both component using different instances of the same service, the most easy way is to mark the service as app-wide singleton using @Injectable({providedIn: 'root'})
pattern. You can read more about singleton service in Angular here.
Upvotes: 2