Angular 4中I18n服务调用方法时未初始化问题求助
解决Angular 4 I18n服务异步加载导致的undefined问题
我一眼就看出问题所在了——这是典型的异步操作时序不匹配!你的I18n服务在构造函数里发起的HttpClient请求是异步的,而组件的ngOnInit生命周期钩子会在这个请求完成、JSON数据还没赋值给对象的时候就执行,这时候调用getKeyValue自然会拿到undefined。
下面给你两种常用的解决方案,你可以根据自己的需求选择:
方案一:让服务返回Observable,组件等待数据加载完成
这种方式不会阻塞应用启动,适合可以接受短暂加载状态的场景。
首先修改你的I18n服务,把翻译数据的请求包装成Observable,并且用shareReplay避免重复请求:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map, shareReplay } from 'rxjs/operators'; @Injectable() export class I18nService { private translations$: Observable<any>; constructor(private http: HttpClient) { // 把请求保存为Observable,shareReplay确保多个订阅者共享同一个请求结果 this.translations$ = this.http.get('/assets/i18n/en.json').pipe(shareReplay(1)); } getKeyValue(key: string): Observable<string> { return this.translations$.pipe( map(translations => translations[key]) ); } }
然后在组件里,你可以选择订阅这个Observable来获取值:
import { Component, OnInit } from '@angular/core'; import { I18nService } from './i18n.service'; @Component({ selector: 'app-your-component', templateUrl: './your-component.component.html' }) export class YourComponent implements OnInit { aboutCardText: string; constructor(private i18n: I18nService) {} ngOnInit() { this.i18n.getKeyValue('about-card-text').subscribe(text => { this.aboutCardText = text; // 这里可以处理依赖这个文本的后续逻辑 }); } }
或者更简单,直接在模板里用async管道自动处理订阅:
<div>{{ i18n.getKeyValue('about-card-text') | async }}</div>
方案二:用APP_INITIALIZER预加载翻译数据
如果你的应用必须在有翻译数据后才能正常显示,那可以用Angular的APP_INITIALIZER,让应用启动前就加载好所有翻译数据,这样组件初始化时数据已经就绪。
首先修改I18n服务,添加一个返回Promise的加载方法:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable() export class I18nService { private translations: any; constructor(private http: HttpClient) {} // 加载翻译数据并返回Promise loadTranslations(): Promise<any> { return this.http.get('/assets/i18n/en.json') .toPromise() .then(data => { this.translations = data; }); } getKeyValue(key: string): string { return this.translations[key]; } }
然后在你的AppModule里配置APP_INITIALIZER:
import { NgModule, APP_INITIALIZER } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule } from '@angular/common/http'; import { I18nService } from './i18n.service'; import { AppComponent } from './app.component'; // 初始化函数,返回一个Promise让Angular等待 export function initI18n(i18n: I18nService) { return () => i18n.loadTranslations(); } @NgModule({ imports: [BrowserModule, HttpClientModule], declarations: [AppComponent], providers: [ I18nService, { provide: APP_INITIALIZER, useFactory: initI18n, deps: [I18nService], multi: true // 允许多个初始化器 } ], bootstrap: [AppComponent] }) export class AppModule {}
这样配置后,Angular会在启动应用前先执行loadTranslations,等Promise完成后才会初始化组件,这时候你在ngOnInit里调用getKeyValue('about-card-text')就不会再出现undefined的问题了。
内容的提问来源于stack exchange,提问作者Marc
相关产品推荐
相关产品推荐

