简体   繁体   中英

Wait for http get request is finished before ngoninit starts angular

Summary

I have an application on Angular. There are i have three components: root , child1 (tabs.component.ts) and child2 (io.component.ts). Also, i have a service that sends get and post requests to tomcat server.

In child1 i have a ngoninit, where i invoke service method get. Also i have ngoninit in child2 . Ngoninit from child1 starts first. In child1 get request fetch data from server too slow and ngoninit from child2 starts before get.subscribe(data=>this.data=data) .

So the problem is that ngoninit method from child2 uses this data and as get request not returned data yet it fills with undefined . So, i have next sequence:

序列(控制台)

Tried

to use async , await , toPromise() but it doesn't work, ngoninit from child2 in all cases loaded before get finished.

Some code

ngOnInit from child1

subsystems: Subsystem[]; //need to ngFor them on html
currentTabId;

constructor(private terminalService: TerminalService) {}

ngOnInit() {
try {
  console.log('child1 ngoninit');
  this.terminalService.getSubsystems().subscribe((data: []) => {
    console.log('get in child1 finished');
    this.subsystems=data;
    this.currentTabId=0;
    this.terminalService.setSubsystem(this.subsystems[this.currentTabId]);
  });
} catch (exception) {
  console.log(exception);
}
}

ngOnInit from child2

here i've got an error: TypeError: Cannot read property 'name' of undefined

constructor(private terminalService: TerminalService) {}

ngOnInit() {
try {
  console.log('child2 ngoninit');
  this.terminalService
    .getResultsBySubsystem(this.terminalService.getSubsystem().name) //here
    .subscribe((data: Command[])=> {
      console.log(data);
      data.forEach((value)=> {
        this.terminalService.setCurrentResult(value.getCommand+'\n'+value.getResult+'\n');
      });
    });
}
catch (exception) {
  console.log(exception);
  this.terminalService.addCurrentResult(this.CONNECTION_ERROR_MSG);
}
}

terminal.service.ts

subsystem: Subsystem;

constructor(private httpClient: HttpClient) {}

getSubsystems() {
  return this.httpClient.get('http://localhost:8080/subsystems');
}

getResultsBySubsystem(name: string) {
  return this.httpClient.get('http://localhost:8080/subsystems/'+name+'/result');
}

getSubsystem() {
  console.log('getSubsystem terminal.service invoking');
  return this.subsystem;
}

setSubsystem(subsystem: Subsystem) {
  console.log('setSubsystem terminal.service invoking ');
  this.subsystem=subsystem;
}

How to wait for get request before ngoninit from child2 will call variable name from subsystem?

Update

thx for your answers. I've tried Resolve , but have

新控制台

as seen, resolve called after get, though this.actr.data , as i understand, must invoke resolve . Confused.

new getSubsystems from terminal.service

import {map} from 'rxjs/operators';


subsystem: Subsystem;

constructor(private httpClient: HttpClient) {}

getSubsystems() {
    console.log('getSubsystems in terminal.service invoking');
    return this.httpClient.get<Subsystem[]>('http://localhost:8080/subsystems')
      .pipe(map(value=>{console.log(value); return value;}));
  }

child1

subsystems: Subsystem[];
currentTabId;

constructor(private terminalService: TerminalService, private actr: ActivatedRoute) {}

ngOnInit() {
    console.log('child1 ngoninit');
    try {
      this.terminalService.setCurrentResult('Connecting...');
      this.actr.data.subscribe((data: []) => { //this
        console.log('get in child1 finished');
        this.subsystems=data;
        console.log(data);
        this.currentTabId=0;
        this.terminalService.setSubsystem(this.subsystems[this.currentTabId]);
      });
    } catch (exception) {
      console.log(exception);
    }
  }

resolve.service

export class ResolverService implements Resolve<any>{

  constructor(private terminalService: TerminalService) { }

  resolve(){
    console.log('resolve');
    return this.terminalService.getSubsystems();
  }
}

resolve.module

import {RouterModule, Routes} from '@angular/router';
import {ResolverService} from './services/resolver.service';

const routes: Routes = [
  {
    path: '',
    component: AppComponent,
    resolve: {
      subsystems: ResolverService
    }
  }
];

export const routing = RouterModule.forRoot(routes);

@NgModule({
  declarations: [],
  imports: [CommonModule],
  exports: [RouterModule],
  providers: [ResolverService]
})
export class ResolverModule { }

app.module

import {ResolverModule} from './resolver.module';
import { routing } from './resolver.module';
import {RouterModule} from '@angular/router';

@NgModule({
  declarations: [
    AppComponent,
    TabsComponent,
    IoComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    BrowserAnimationsModule,
    HttpClientModule,
    routing
  ],
  exports: [RouterModule],
  providers: [ResolverModule],
  bootstrap: [AppComponent]
})
export class AppModule { }

What's wrong with this?

I solved it using APP_INITIALIZER

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import {APP_INITIALIZER, NgModule} from '@angular/core';
import { AppComponent } from './app.component';
import {FormsModule} from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TabsComponent } from './tabs/tabs.component';
import {HttpClientModule} from '@angular/common/http';
import { IoComponent } from './io/io.component';
import {AppConfig} from './config/app.config';

export function loadConfig(config: AppConfig) {
  return () => config.load();
}

@NgModule({
  declarations: [
    AppComponent,
    TabsComponent,
    IoComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    BrowserAnimationsModule,
    HttpClientModule,
  ],
  bootstrap: [AppComponent],
  providers: [
    AppConfig,
    {provide: APP_INITIALIZER, useFactory: loadConfig, deps: [AppConfig], multi: true}
  ]
})
export class AppModule {
}

create config file and load() function there to load get request before components' constructors will be loaded

app.config.ts

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Subsystem} from '../data/subsystem';
import {map} from 'rxjs/operators';

@Injectable()
export class AppConfig {

  private subsystems: Subsystem[] = null;
  private subsystem: Subsystem = null;

  constructor(private http: HttpClient) {

  }

  load() {
    return new Promise((resolve) => {
      this.http
        .get('http://localhost:8080/subsystems')
        .pipe(map(value=>value))
        .subscribe((data: Subsystem[]) => {
          this.subsystems = data;
          resolve(true);
        });
    });
  }

  public getSubsystem() {
    return this.subsystem;
  }

  public setSubsystem(subsystem: Subsystem) {
    this.subsystem=subsystem;
  }

  public getSubsystems() {
    return this.subsystems;
  }

  public setSubsystems(subsystems: Subsystem[]) {
    this.subsystems=subsystems;
  }
}

use initialized data

tabs.component.ts

import {Component, OnInit} from '@angular/core';
import {Subsystem} from '../data/subsystem';
import {AppConfig} from '../config/app.config';

@Component({
  selector: 'app-tabs-component',
  templateUrl: 'tabs.component.html',
  styleUrls: ['./tabs.component.css'],
})

export class TabsComponent implements OnInit {

  subsystems: Subsystem[];
  subsystem: Subsystem;
  currentTabId;

  constructor(private config: AppConfig) {
  }

  ngOnInit() {
    this.currentTabId=0;
    this.subsystems=this.config.getSubsystems();
    this.config.setSubsystem(this.subsystems[this.currentTabId]);
    this.subsystem=this.config.getSubsystem();
  }

  tabPressed(id) {
    this.currentTabId=id;
    this.config.setSubsystem(this.subsystems[id]);
    this.subsystem=this.config.getSubsystem();
  }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM