Reputation: 127
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:
to use async
, await
, toPromise()
but it doesn't work, ngoninit from child2 in all cases loaded before get finished.
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?
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?
Upvotes: -1
Views: 2201
Reputation: 127
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();
}
}
Upvotes: 3