admin管理员组

文章数量:1405502

This is my Angular App. My app will get data from API (temporarily in JSON file) and show in many another sibling ponent. So I decide to create a category.service.ts that I get and store data in. I using APP_INITIALIZER to run this service first when my app started. But there is a problem that: This service is running first, AppComponent runs before service get data done. So my view have empty of data.

If I click button routing to this ponent, everything run perfect. But when I go to this ponent by url path or F5(refresh page), nothing is shown

category.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/mon/http';
@Injectable({
  providedIn: 'root'
})
export class CategoryService {
  DATA_CATEGORIES = 'assets/categories.json';
  private _categories = [];
  constructor(private http: HttpClient) {
  }
  get categories() {
    return this._categories;
  }
  Init(): Promise<any> {
    return new Promise<void>(resolve => {
      this.http.get(this.DATA_CATEGORIES).subscribe(data => {
        this._categories = Array.from(Object.keys(data), k => data[k]);
        console.log("load data...");

      });
      resolve();
    });
  }
}

This is my Angular App. My app will get data from API (temporarily in JSON file) and show in many another sibling ponent. So I decide to create a category.service.ts that I get and store data in. I using APP_INITIALIZER to run this service first when my app started. But there is a problem that: This service is running first, AppComponent runs before service get data done. So my view have empty of data.

If I click button routing to this ponent, everything run perfect. But when I go to this ponent by url path or F5(refresh page), nothing is shown

category.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/mon/http';
@Injectable({
  providedIn: 'root'
})
export class CategoryService {
  DATA_CATEGORIES = 'assets/categories.json';
  private _categories = [];
  constructor(private http: HttpClient) {
  }
  get categories() {
    return this._categories;
  }
  Init(): Promise<any> {
    return new Promise<void>(resolve => {
      this.http.get(this.DATA_CATEGORIES).subscribe(data => {
        this._categories = Array.from(Object.keys(data), k => data[k]);
        console.log("load data...");

      });
      resolve();
    });
  }
}

app.module.ts

export function initializeCategoryService(catService: CategoryService) {
  return (): Promise<any> => {
    return catService.Init();
  }
}
@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    StoriesFilterPipe,
    ViewStoryComponent,
    ViewCatComponent,
    FrontEndComponent,
    SearchComponent,
    BackEndComponent,
    CrudStoryFormComponent,
    CrudStoryComponent,
    JwPaginationComponent,
    CrudCatComponent,
    CrudCatFormComponent,
    CrudCatSearchResultComponent,
    CatListComponent

  ],
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule,
    HttpClientModule,
  ],
  providers: [
    StoryService,
    CategoryService,
    {
      provide: APP_INITIALIZER, useFactory: initializeCategoryService, deps: [CategoryService], multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Share Improve this question edited Feb 10, 2020 at 10:21 Nguyen Duc Binh asked Feb 10, 2020 at 9:09 Nguyen Duc BinhNguyen Duc Binh 131 gold badge1 silver badge4 bronze badges 1
  • try to use *ngIf in your app.ponent.html until your data is loaded – chana Commented Feb 10, 2020 at 9:17
Add a ment  | 

3 Answers 3

Reset to default 2

What I will suggest is to use Observable

like in your category service

import { Observable, Subject } from 'rxjs';
export class CategoryService {
    private loadDataSub = new Subject<any>();
    loadDataObservable$ = this.loadDataSub.asObservable();

    emitLoadDataSuccess() {
        this.loadDataSub.next();
    }

Init(): Promise<any> {
    return new Promise<void>(resolve => {
      this.http.get(this.DATA_CATEGORIES).subscribe(data => {
        this._categories = Array.from(Object.keys(data), k => data[k]);
        console.log("load data...");
        this.emitLoadDataSuccess(); // here we are emitting event
      });
      resolve();
    });
 }
}

And In your ponent

export class AppComponent implements OnInit {
    constructor(private categoryService: CategoryService) {
          this.categoryService.loadDataObservable$.subscribe(() => {
              // here you can get data, this will only trigger when data is loaded from API
          });
    }
}

This is mon case - i.e. you shows page while data is not avaliable yet - at slow and bad connections for instance, and it can do even more - connection was broken and data was nto recieved.

So, your page should be able to show not only data recieved, but also two another states: loading and error.

(So the advise is "add loader").

// data.service.ts

import { Injectable } from "@angular/core";
import { HttpClient, HttpClientModule } from "@angular/mon/http";

@Injectable()
export class DataService {
  private _categories = [];

  constructor(private http: HttpClient) {}
  get categories() {
    return this._categories;
  }
  getData(): Promise<any[]> {
    return new Promise<any[]>(resolve => {
      this.http.get('https://api.myjson./bins/18qku4').subscribe(data => {
        this._categories = Array.from(Object.keys(data), k => data[k]);
        console.log("load data...");
        resolve(this._categories);
      });
    });
  }
}


// app.module.ts

import { NgModule, APP_INITIALIZER } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";
import { RouterModule } from "@angular/router";
import { ListDataComponent } from "./list-data/list-data.ponent";
import { AppComponent } from "./app.ponent";
import { DataService } from "./data.service";
import { HttpClientModule } from "@angular/mon/http";
import {DetailComponent} from './detail/detail.ponent'

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    RouterModule.forRoot([
      { path: "", ponent: ListDataComponent },
      { path: "detail", ponent: DetailComponent }
    ])
  ],
  declarations: [AppComponent, ListDataComponent,DetailComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}


//list-data.ponent.ts

import { Component, OnInit } from "@angular/core";
import { DataService } from "../data.service";

@Component({
  selector: "app-list-data",
  templateUrl: "./list-data.ponent.html",
  styleUrls: ["./list-data.ponent.css"],
  providers: [DataService],
})
export class ListDataComponent implements OnInit {
  categories = [];
  constructor(service: DataService) {
    service.getData().then(data => {
      debugger;
      this.categories = data;
    });
  }
  ngOnInit() {}
}

There are alternatives to resolve this issue:

  • One is you can use a loader which you can display until the service call finishes.

  • Second is you can use *ngIf="categories?.length" which will keep your ponent hides until your service call finishes.

I hope it will resolve your issue.

本文标签: javascriptHow to load data service done first when app starting in Angular 8Stack Overflow