Guna M
Guna M

Reputation: 53

Angular 4 - getting data from service before component getting executed

I am a newbie and not sure what is wrong with my approach, please suggest how to resolve below issue.

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 the update this.configStringSource.next(config). Right after that it redirects to this.router.navigate(['/clone/status']), there I am not getting data from config.service.ts

If I use ngAfterContentChecked, config-resolver.service.ts getting executed befor status.component.ts and config-resolver.service.ts fetching the correct data as expected, but I am not able to get the same data from status.component.ts using this.route.data.subscribe, I am getting "undefined".

Please see the complete code below and your help would be appreciated.

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 Subject<Config>();

    // 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());
    }

    getConfig(configID: string) {
      this.configsString$.subscribe(
        data => {
console.log('config-resolver.service.ts configsString$ = ', data[1]);
          return data[1];
      });
    }
}

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 {
    this.configService.configsString$.subscribe(
        data => {
console.log('config-resolver.service.ts data[1] = ', data[1]);
        this.config = data[1];
console.log('config-resolver.service.ts this.config = ', this.config);
     });
console.log('config-resolver.service.ts this.config = ', this.config);
     return this.config;
  }
}

app.module.ts

...
...
import { ConfigService } from './_services/config.service';
import { ConfigResolver } from './_services/config-resolver.service';

@NgModule({
  declarations: [
...
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    AppRouting,
    BrowserAnimationsModule,
  ],
  providers: [ConfigService, ConfigResolver],
  bootstrap: [AppComponent]
})
export class AppModule { }

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);

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 AfterContentChecked {

  configs: string;
  config: Config;
  servers: Array<any>;
  server: { id: number; name: string; status: string; };

  constructor(private configService:ConfigService,
              private route: ActivatedRoute,
              private router: Router) { }

  ngAfterContentChecked() {
    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);
        }
    );
  }
}

console.log output

accordion.component.ts:54

accordion.component.ts this.accordions = undefined core.es5.js:2925

Angular is running in the development mode. Call enableProdMode() to enable the production mode. config-resolver.service.ts:69

config-resolver.service.ts this.config = undefined clone.component.ts:20

clone ngOnInit this.config = undefined status.component.ts:79

status.component.ts data = undefined status.component.ts:80

status.component.ts this.config = undefined status.component.ts:79

status.component.ts data = undefined status.component.ts:80

status.component.ts this.config = undefined status.component.ts:79

status.component.ts data = undefined status.component.ts:80

status.component.ts this.config = undefined config-resolver.service.ts:64

config-resolver.service.ts data[1] = {configID: "PRODDB_TO_DEVDB", sourceDbNodes: "dbnode21", targetDbNodes: "dbnode22"} config-resolver.service.ts:66

config-resolver.service.ts this.config = {configID: "PRODDB_TO_DEVDB", sourceDbNodes: "dbnode21", targetDbNodes: "dbnode22"} app.component.ts:27

app.component.ts sourceDbNode = dbnode11 accordion.component.ts:54

status.component.ts data = undefined status.component.ts:80

status.component.ts this.config = undefined

--

Thank you in advance for the help.

Upvotes: 1

Views: 1000

Answers (1)

JB Nizet
JB Nizet

Reputation: 691755

Your resolver is wrong:

this.configService.configsString$.subscribe(
  data => {
    this.config = data[1];
  });
  return this.config;
}

That makes no sense. You're getting a value asynchronous, by subscribing to an observable, so that the callback is executed, later, when the data is available, and after this asynchonous call is made, you return immediately.

As the return type of themethod shows, just return an observable, and the router itself will subscribe, and navigate to your component only once the data is available:

return this.configService.configsString$.map(
  data => data[1];
});

That is only the first problem, though. Since you're subscribing to a subject, the router will wait until the next event is emitted by the subject. But there will never be one, because you've just emitted the last event before, when updating the config. You need to learn more about how observables work.

Upvotes: 1

Related Questions