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

3

我已经创建了一个服务,从服务器获取一些数据。这些数据对于制作我的组件是必需的,并且应该只被调用一次,因为会对服务器造成巨大负载。当我在app模块提供程序中使用服务时,它会等待获取所需的数据,并成功获取,因为我可以记录下来。但在此后,当我想要在组件中获取数据时,它返回Undefined!

我尝试通过删除providers中的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个回答

5
你所要求的是满足组件需求的一种方法。这可以通过在你的路由中使用一个解析器来实现。
关键要素如下: 服务,它拥有你需要的信息(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;
    }
}

Resolver是在您的路由之前运行并解决所需内容的代码片段。当观察者完成时,Resolver也会完成。

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

路由,也就是用resolver保护你的路由:

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

您的组件和服务必须在模块的providers部分进行注册。然后,您可以在任何由解析器保护的组件中自由使用ApplicationServicegetCachedData()方法。
请注意,解析器可能会被调用多次,但是由于我们在服务中缓存了数据,因此我们不会每次都请求它们。 更新 这种方法适用于只有1级子模块的情况。如果情况变得更加复杂或者您想要多个模块使用这些数据,最好创建一个父模块并解析整个路由,然后在该路由模块中编写路由配置。 例如:
const routes: Routes = [
  {
    path: 'login',
    component: LoginComponent
  },
  {
    path: '',
    component: MyLayout,
    canActivate: [AuthGuard],
    children: [
      {
        path: 'dashboard',
        children: [
          {
            path: '',
            component: DashboardComponent
          },
        ]
      },
      {
        path: 'publisher',
        loadChildren: './modules/publisher/publisher.module#PublisherModule'
      }
    ],
    resolve: {
      data: AuthenticatedAppResolver
    }
  },
]

0

我猜你的方法太复杂了。而且你从来没有返回response的值。试试这种方式:

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

response.json() 已经返回一个 Promise。

甚至更简短:

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

实际上我不想在getAdminInitInfo函数中返回它。它应该在另一个函数中返回,而不需要再次调用API。实际上,我想要实现的是只调用一次API,并在多个组件中多次使用其数据。我编辑了问题以使其更清晰。 - W1ldworm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接