در برنامههای کاربردی بر پایه Angular گاها نیاز است اعمالی را قبل از بارگذاری آغازین نرم افزار انجام دهید. این موارد میتوانند خواندن اطلاعات پیکربندی از یک فایل json. باشند و یا گرفتن دادههایی از سرور بکاند و استفاده از آنها برای ایجاد محدودیت در بعضی از قسمتها.
از +Angular 4 با بکار گیری توکن APP_INITIALIZER در ماژول آغازین برنامه (app.module)، امکان معرفی و ثبت تابعی را جهت اجرا، در ابتدای چرخه حیات برنامه، خواهیم داشت. برای روشن شدن مطلب، مثالی از خواندن اطلاعات پیکربندی واقع در یک فایل جیسون را در ذیل پیاده سازی میکنیم.
در بسیاری موارد نیاز داریم که پس از بیلد پروژه، امکان تغییر آدرس نهایی هاست سمت سرور را داشته باشیم و بر اساس آن بتوانیم Web Api Urlها را در سرویسهای Angular به روز کنیم. برای این کار فایلی را به نام config.json با محتویات ذیل ایجاد میکنیم (آدرس هاست را مطابق سرور خود، تغییر دهید):
{
"host": "http://localhost:8008"
}
سپس سرویسی که وظیفهی خواندن اطلاعات را بر عهده دارد، ترجیحا در بخش
Core Modules پروژه با کد ذیل، ایجاد میکنیم:
import { Observable } from 'rxjs/Observable';
import { Inject, Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
@Injectable()
export class AppConfigService {
constructor(private http: Http) { }
private config: Object = null;
get apiRoot() {
return this.getProperty('host'); // <--- THIS GETS CALLED FIRST
}
load(): Promise<any> {
console.log('get user called');
const promise = this.http.get('assets/config.json').map((res) => res.json()).toPromise();
promise.then(config => {
this.config = config; // <--- THIS RESOLVES AFTER
console.log(this.config);
});
return promise;
}
private getProperty(property: string): any {
//noinspection TsLint
if (!this.config) {
throw new Error('Attempted to access configuration property before configuration data was loaded, please implemented.');
}
if (!this.config[property]) {
throw new Error(`Required property ${property} was not defined within the configuration object. Please double check the
assets/config.json file`);
}
return this.config[property];
}
}
حال در app.module توسط APP_INITIALIZER، متد Load سرویس را برای مقدار دهی خاصیت apiRoot سرویس صدا میزنیم. بنابراین ابتدا تابع init را تکمیل میکنیم:
export function init(config: AppConfigService) {
return () => {
return config.load();
};
}
سپس در قسمت Providers ماژول آغازین (app.module)، مانند کد زیر اقدام میکنیم:
Providers:[
…,
AppConfigService,
{
provide: APP_INITIALIZER,
useFactory: init,
multi: true,
deps: [AppConfigService]
}
…,
]
حال میتوانیم سرویس AppConfigService را در متد سازندهی سرویسهای خود تزریق کرده و از خاصیت apiRoot جهت به روز رسانی آدرسهای خود استفاده کنیم:
@Injectable()
export class DashboardService {
private tagUrl = '';
constructor(private http: Http,private AppConfig:AppConfigService) {
this.tagtUrl = this.AppConfig.apiRoot+"/myApiUrl";
….
}
نکته تکمیلی: میتوان چندین تابع را به همین روش در نقطهی آغازین برنامه، جهت اجرا معرفی کرد. کافی است useFactory را به نام تابع مورد نظر خود و خاصیت deps را که اشاره به نوع پارامتر ورودی تابع دارد (در اینجا سرویس AppConfigService) مقدار دهی کنید:
{
provide: APP_INITIALIZER,
useFactory: init,
multi: true,
deps: [AppConfigService]
},
{
provide: APP_INITIALIZER,
useFactory: initIdentity,
multi: true,
deps: [IdentityService]
},
AppConfigService,
IdentityService,