Angular gurus your help is greatly appreciated in this regard.
Here is what I am trying to do, when I access main app page it calls getConfigs() from config.service.ts and get the data from backend then updates this.configStringSource.next(config). Right after that I am trying to redirect it to this.router.navigate(['/clone/status']), but redirect is not happening.
app.routing.ts
import { Routes, RouterModule } from '@angular/router';
import { CloneComponent } from './clone/clone.component';
import { StatusComponent } from './status/status.component';
import { ConfigurationComponent } from './configuration/configuration.component';
import { LogsComponent } from './logs/logs.component';
import { ConfigResolver } from './_services/config-resolver.service';
const appRoutes: Routes = [
{ path: 'clone', component: CloneComponent, children: [
{path: 'status', component: StatusComponent, resolve: {config: ConfigResolver} },
]
},
{ path: 'logstream', component: LogstreamComponent },
];
export const AppRouting = RouterModule.forRoot(appRoutes);
config.ts
export class Config {
configID: string;
sourceDbNodes: string;
targetDbNodes: string;
}
config.service.ts
import { Injectable, OnInit } from '@angular/core';
import { Http, Headers, Response } from '@angular/http';
//import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Router } from '@angular/router';
import {Subject} from 'rxjs/Subject';
import { Config } from '../_models/config';
@Injectable()
export class ConfigService {
// Observable string source
private configsStringSource = new BehaviorSubject<Config>({ configID: "", sourceDbNodes: "", targetDbNodes: ""});
// Observable string stream
configsString$ = this.configsStringSource.asObservable();
// Service message commands
updateConfigs(configs: Config) {
this.configsStringSource.next(configs)
}
constructor(private http: Http, private router:Router) { }
getConfigs() {
let headers = new Headers();
headers.append('Content-Type','application/json');
return this.http.get('http://localhost:8080/sample1/api/config', { headers: headers })
.map((response: Response) => response.json());
}
}
config-resolver.service.ts
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Injectable } from '@angular/core';
import { ConfigService } from './config.service';
import { Config } from '../_models/config';
interface Server {
id: number;
name: string;
status: string;
}
@Injectable()
export class ConfigResolver implements Resolve<Config> {
config: Config;
constructor(private configService: ConfigService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Config> | Promise<Config> | Config {
return this.configService.configsString$.map(
data => data[1]);
}
}
app.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Config } from './_models/config';
import { ConfigService } from './_services/config.service';
@Component({
moduleId: module.id.toString(),
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
configs: Config[];
constructor(private router:Router, private configService:ConfigService ) { }
title = 'Angular 4 Proeject';
private getConfigs() {
this.configService.getConfigs().subscribe(configs => {
this.configs = configs;
this.configService.updateConfigs(configs);
console.log('app.component.ts sourceDbNode = '+this.configs[0].sourceDbNodes);
});
}
ngOnInit() {
this.getConfigs();
this.router.navigate(['/clone/status']);
}
}
status.component.ts
import { Component, Input, OnInit, AfterContentChecked } from '@angular/core';
import { ActivatedRoute, Params, Router, Data } from '@angular/router';
import { Config } from '../_models/config';
import { ConfigService } from '../_services/config.service';
@Component({
selector: 'app-status',
template: `
<p>
status Works! {{config}}
</p>
`,
styleUrls: ['./status.component.scss']
})
export class StatusComponent implements OnInit {
configs: string;
config: Config;
servers: Array<any>;
server: { id: number; name: string; status: string; };
constructor(private configService:ConfigService,
private route: ActivatedRoute,
private router: Router) { }
ngOnInit() {
this.route.data.subscribe(
(data: Data) => {
this.config = data['config'];
console.log('status.component.ts data = ', data['config']);
console.log('status.component.ts this.config = ', this.config);
}
);
}
}
Your issue is that resolved observables need to complete, so you just need to add .take(1) or .first() to your resolver observable like:
However, there's a much cleaner way of loading a config service:
then in your app module, import both ConfigService and ConfigServiceInitProvider and provide them both like this:
this will ensure that your config is loaded before pretty much anything else, and you can inject your config service where ever it's needed and the config will always be available in a synchronous manner.