在Angular 6中初始化应用之前获取所需数据

时间:2019-01-27 09:14:49

标签: angular typescript rxjs angular6 observable

我创建了一个服务,该服务从服务器获取一些数据,它们是制作我的组件所必需的,应该被调用一次,因为这会给服务器带来巨大的负担。当我在应用程序模块提供程序中使用服务时,它会等到获取所需数据后才成功获取它,因为我可以对其进行记录,但是此后,当我想要在组件中获取它时,它将返回未定义!

我试图通过从提供者中删除UserService来将服务的初始化次数减少到1,但这会导致错误。

获取信息的功能:

getAdminInitInfo(): Promise<any> {
var vm = this;
const promise = this.http.get<AdminInit>(baseUrl + '/panel/admin/initial/')
  .toPromise()
  .then(
    response => {
      vm.adminInitInfo = response;
      console.log(response);
    }
  )
return promise;
}

将在APP_INITIALIZATION中使用的工厂:

export function adminProviderFactory(provider: UserService) {
  return () => provider.getAdminInitInfo();
}

AppModule提供程序:

providers: [
    AuthGuardService,
    AuthService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: InterceptorService,
      multi: true
    },
    UserService,
    {
      provide: APP_INITIALIZER,
      useFactory: adminProviderFactory,
      deps: [UserService],
      multi: true
    }
]

我在组件中获得的数据日志:

user.service.ts:23 service started 
user.service.ts:23 service started 
user.service.ts:70 {filters: {…}}
user.service.ts:78 undefined
overview.component.ts:19 undefined

在执行此过程之后,只要我需要使用此函数,就可以随时返回adminInitInfo:

getSomethingData() {
    var data = {};
    console.log(this.adminInitInfo);
    if (!this.adminInitInfo) {
      return undefined;
    }
    data['something'] = this.adminInitInfo.filters.something;
    data['something2'] = this.adminInitInfo.filters.something2;
    data['something3'] = this.adminInitInfo.filters.something3;
    return data;
}

在诸如以下的组件中

ngOnInit() {
    this.params = this.userService.getSomethingData();
}

2 个答案:

答案 0 :(得分:2)

您要问的是一种满足组件要求的方法。这可以通过在路由中使用解析器来完成。

关键成分如下:

服务,其中包含您所需的信息(ApplicationData是我的一类,其中包含有关应用程序的信息,例如用户名,版本等...,与以下信息无关此上下文):

@Injectable()
export class ApplicationService {
    private cachedData: ApplicationData = null;

    public constructor(/* place here your dependencies */) {
    }

    public initializeApplicationData(): Observable<ApplicationData> {
        // return here the observable that builds your application data, like for example getting the user, and joining it with the version and tap it:

        // if we already have the data, don't ask anything and simply return it
        if (this.cachedData)
            return of(this.cachedData);

        return /* your service that builds the application data */.getApplicationData().pipe(
            tap(x=> this.cachedData = x));
    }

    public getCachedData(): ApplicationData {
       return this.cachedData;
    }
}

解析器,它是在路由发生并解决所需所有内容之前运行的代码。观察者完成时,解析程序完成。

@Injectable()
export class AuthenticatedAppResolver implements Resolve<Observable<ApplicationData>> {

    public constructor(private applicationService: ApplicationService) { }

    public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ApplicationData> {
        return this.applicationService.initializeApplicationData();
    }
}

路由,即只需使用解析器保护您的路由即可:

    path: '',
    component: MyLayout,
    canActivate: [AuthGuard],
    children: [
        { path: 'home', component: HomeComponent },
        { path: 'page1', component: Page1Component },
        { path: 'page', component: APage2Component },
    ],
    resolve: { data: AuthenticatedAppResolver },

您的组件和服务必须在模块的providers部分中进行注册。然后,您可以在解析器保护的任何组件中自由使用getCachedData()的{​​{1}}方法。

请注意,解析程序可能会被调用多次,但是,由于我们在服务中进行缓存,因此我们不会每次都询问它们。

更新

直到您拥有1级子模块,此方法才有效。如果变得更加复杂,或者您希望多个模块使用这些数据,则最好创建一个父模块并解析整个路由,然后在该路由模块中写入路由。 示例:

ApplicationService

答案 1 :(得分:0)

我想您的方法太复杂了。而且,您永远不会返回response的值。改为尝试这种方式:

getAdminInitInfo(): Promise<any> {
    return this.http.get<AdminInit>(baseUrl + '/panel/admin/initial/')
    .toPromise()
    .then(
         response => { return response.json(); }
    );
}

response.json()已经返回了诺言。

或更短:

getAdminInitInfo(): Promise<any> {
    return this.http.get<AdminInit>(baseUrl + '/panel/admin/initial/')
    .toPromise()
    .then(
         response => response.json()
    );
}