在Angular 2中定义全局常量
在Angular 1.x中,你可以像这样定义常量:
angular.module('mainApp.config', []) .constant('API_ENDPOINT', 'http://127.0.0.1:6666/api/')
在Angular2(与打字稿)等价? 我只是不想在我的所有服务中重复一遍又一遍的API基础url。
Angular 2最终版本下面的变化对我有用:
export class AppSettings { public static API_ENDPOINT='http://127.0.0.1:6666/api/'; }
然后在服务中:
import {Http} from 'angular2/http'; import {Message} from '../models/message'; import {Injectable} from 'angular2/core'; import {Observable} from 'rxjs/Observable'; import {AppSettings} from '../appSettings'; import 'rxjs/add/operator/map'; @Injectable() export class MessageService { constructor(private http: Http) { } getMessages(): Observable<Message[]> { return this.http.get(AppSettings.API_ENDPOINT+'/messages') .map(response => response.json()) .map((messages: Object[]) => { return messages.map(message => this.parseData(message)); }); } private parseData(data): Message { return new Message(data); } }
angular度团队提供的configuration解决scheme可以在这里find。
我已经成功地实现了它,这里是所有相关的代码:
1)app.config.ts
import { OpaqueToken } from "@angular/core"; export let APP_CONFIG = new OpaqueToken("app.config"); export interface IAppConfig { apiEndpoint: string; } export const AppConfig: IAppConfig = { apiEndpoint: "http://localhost:15422/api/" };
2)app.module.ts
import { APP_CONFIG, AppConfig } from './app.config'; @NgModule({ providers: [ { provide: APP_CONFIG, useValue: AppConfig } ] })
3)your.service.ts
import { APP_CONFIG, IAppConfig } from './app.config'; @Injectable() export class YourService { constructor(@Inject(APP_CONFIG) private config: IAppConfig) { // You can use config.apiEndpoint now } }
现在,您可以在不使用string名称的情况下使用您的接口来进行静态检查。
你当然可以将接口和常量分开,以便能够在生产和开发中提供不同的值
在Angular2中,你有以下提供的定义,它允许你设置不同种类的依赖关系:
provide(token: any, {useClass, useValue, useExisting, useFactory, deps, multi}
比较angular1
app.service
中的app.service等同于useClass
中的useClass。
app.factory
中的app.factory等同于useFactory
中的useFactory。
app.constant
和app.value
已经被简化为使用更less约束的值。 即没有config
块了。
app.provider
– 在Angular 2中没有等价物。
例子
使用根注射器进行设置:
bootstrap(AppComponent,[provide(API_ENDPOINT, { useValue='http://127.0.0.1:6666/api/' })]);
或者使用组件的注入器进行设置:
providers: [provide(API_ENDPOINT, { useValue: 'http://127.0.0.1:6666/api/'})]
provide
简短的手段:
var injectorValue = Injector.resolveAndCreate([ new Provider(API_ENDPOINT, { useValue: 'http://127.0.0.1:6666/api/'}) ]);
使用喷油器,获得价值很简单:
var endpoint = injectorValue.get(API_ENDPOINT);
虽然使用一个带有一个string常量的AppSettings类作为ApiEndpoint的方法是有效的,但这并不理想,因为在unit testing时,我们无法将这个真正的ApiEndpointreplace为其他值。
我们需要能够将这个API端点注入到我们的服务中(想想把服务注入到另一个服务中)。 我们也不需要为此创build一个完整的类,我们所要做的就是向我们的ApiEndpoint的服务中注入一个string。 为了完成像素位的优秀回答, 下面是在Angular 2中如何完成的完整代码:
首先,我们需要告诉Angular如何在我们的应用程序中请求时提供我们的ApiEndpoint实例(将其视为注册依赖关系):
bootstrap(AppComponent, [ HTTP_PROVIDERS, provide('ApiEndpoint', {useValue: 'http://127.0.0.1:6666/api/'}) ]);
然后在服务中,我们将这个ApiEndpoint注入到服务构造函数中,Angular将根据我们上面的注册为我们提供:
import {Http} from 'angular2/http'; import {Message} from '../models/message'; import {Injectable, Inject} from 'angular2/core'; // * We import Inject here import {Observable} from 'rxjs/Observable'; import {AppSettings} from '../appSettings'; import 'rxjs/add/operator/map'; @Injectable() export class MessageService { constructor(private http: Http, @Inject('ApiEndpoint') private apiEndpoint: string) { } getMessages(): Observable<Message[]> { return this.http.get(`${this.apiEndpoint}/messages`) .map(response => response.json()) .map((messages: Object[]) => { return messages.map(message => this.parseData(message)); }); } // the rest of the code... }
这是我最近的经验:
- @ angular / cli:1.0.0
- 节点:6.10.2
- @ angular / core:4.0.0
我在这里按照官方和更新的文档:
https://angular.io/docs/ts/latest/guide/dependency-injection.html#!#dependency-injection-tokens
似乎OpaqueToken现在已被弃用,我们必须使用InjectionToken ,所以这些是我的文件运行像一个魅力:
app-config.interface.ts
export interface IAppConfig { STORE_KEY: string; }
app-config.constants.ts
import { InjectionToken } from "@angular/core"; import { IAppConfig } from "./app-config.interface"; export const APP_DI_CONFIG: IAppConfig = { STORE_KEY: 'l@_list@' }; export let APP_CONFIG = new InjectionToken< IAppConfig >( 'app.config' );
app.module.ts
import { APP_CONFIG, APP_DI_CONFIG } from "./app-config/app-config.constants"; @NgModule( { declarations: [ ... ], imports: [ ... ], providers: [ ..., { provide: APP_CONFIG, useValue: APP_DI_CONFIG } ], bootstrap: [ ... ] } ) export class AppModule {}
my-service.service.ts
constructor( ..., @Inject( APP_CONFIG ) private config: IAppConfig) { console.log("This is the App's Key: ", this.config.STORE_KEY); //> This is the App's Key: l@_list@ }
结果是干净的,有没有在控制台上的警告感谢约翰帕帕在这个问题上最近的评论:
https://github.com/angular/angular-cli/issues/2034
关键是在不同的文件中实现接口。
所有的解决scheme似乎都很复杂。 我正在寻找这种情况下最简单的解决scheme,我只想使用常量。 常量很简单。 有什么和下面的解决scheme有关的吗?
app.const.ts
'use strict'; export const dist = '../path/to/dist/';
app.service.ts
import * as AppConst from '../app.const'; @Injectable() export class AppService { constructor ( ) { console.log('dist path', AppConst.dist ); } }
感谢您的回复。
在Angular 4中,你可以使用环境类来保存所有的全局variables。
你有默认的environment.ts和environment.prod.ts。
例如
export const environment = { production: false, apiUrl: 'http://localhost:8000/api/' };
然后在您的服务:
import { environment } from '../../environments/environment'; ... environment.apiUrl;
只需使用一个Typescript常量
export var API_ENDPOINT = 'http://127.0.0.1:6666/api/';
你可以在dependency injection器中使用它
bootstrap(AppComponent, [provide(API_ENDPOINT, {useValue: 'http://127.0.0.1:6666/api/'}), ...]);
在我的提供者中,我一直在做下面的事情:
import {Injectable} from '@angular/core'; @Injectable() export class ConstantService { API_ENDPOINT :String; CONSUMER_KEY : String; constructor() { this.API_ENDPOINT = 'https://api.somedomain.com/v1/'; this.CONSUMER_KEY = 'someReallyStupidTextWhichWeHumansCantRead' } }
然后我可以在任何地方访问所有常量数据
import {Injectable} from '@angular/core'; import {Http} from '@angular/http'; import 'rxjs/add/operator/map'; import {ConstantService} from './constant-service'; //This is my Constant Service @Injectable() export class ImagesService { constructor(public http: Http, public ConstantService: ConstantService) { console.log('Hello ImagesService Provider'); } callSomeService() { console.log("API_ENDPOINT: ",this.ConstantService.API_ENDPOINT); console.log("CONSUMER_KEY: ",this.ConstantService.CONSUMER_KEY); var url = this.ConstantService.API_ENDPOINT; return this.http.get(url) } }
如果您使用的是我推荐的Webpack ,则可以为不同的环境设置常量。 当每个环境具有不同的常量值时,这是特别有价值的。
您的/config
目录下可能会有多个webpack文件(例如,webpack.dev.js,webpack.prod.js等)。 那么你将有一个custom-typings.d.ts
你将它添加到那里。 以下是每个文件中遵循的一般模式以及组件中的示例用法。
的WebPack。{ENV}的.js
const API_URL = process.env.API_URL = 'http://localhost:3000/'; const JWT_TOKEN_NAME = "id_token"; ... plugins: [ // NOTE: when adding more properties, make sure you include them in custom-typings.d.ts new DefinePlugin({ 'API_URL': JSON.stringify(API_URL), 'JWT_TOKEN_NAME': JSON.stringify(JWT_TOKEN_NAME) }),
定制typings.d.ts
declare var API_URL: string; declare var JWT_TOKEN_NAME: string; interface GlobalEnvironment { API_URL: string; JWT_TOKEN_NAME: string; }
零件
export class HomeComponent implements OnInit { api_url:string = API_URL; authToken: string = "Bearer " + localStorage.getItem(JWT_TOKEN_NAME)}); }
使用在构build过程中生成的属性文件非常简单。 这是Angular CLI使用的方法。 为每个环境定义属性文件,并在构build过程中使用命令来确定将哪个文件复制到您的应用程序。 然后只需导入要使用的属性文件。
https://github.com/angular/angular-cli#build-targets-and-environment-files
你可以为你的全局variables创build一个类,然后像这样导出这个类:
export class CONSTANT { public static message2 = [ { "NAME_REQUIRED": "Name is required" } ] public static message = { "NAME_REQUIRED": "Name is required", } }
在创build和导出CONSTANT
类之后,您应该在要使用的类中导入此类,如下所示:
import { Component, OnInit } from '@angular/core'; import { CONSTANT } from '../../constants/dash-constant'; @Component({ selector : 'team-component', templateUrl: `../app/modules/dashboard/dashComponents/teamComponents/team.component.html`, }) export class TeamComponent implements OnInit { constructor() { console.log(CONSTANT.message2[0].NAME_REQUIRED); console.log(CONSTANT.message.NAME_REQUIRED); } ngOnInit() { console.log("oninit"); console.log(CONSTANT.message2[0].NAME_REQUIRED); console.log(CONSTANT.message.NAME_REQUIRED); } }
你可以在constructor
或ngOnInit(){}
,或者在任何预定义的方法中使用。
AngularJS的module.constant
没有定义标准意义上的常量。
虽然它作为一个提供者注册机制自己站立,但最好在相关的module.value
( $provide.value
)函数的上下文中理解。 官方文件明确指出了用例:
用$注入器注册一个值服务,如string,数字,数组,对象或函数。 这是注册服务的简称,其提供者的$ get属性是不带参数的工厂函数,并返回值服务。 这也意味着无法将其他服务注入价值服务。
把这个和module.constant
( $provide.constant
module.constant
)的文档比较一下,它也清楚地说明了用例(重点是我的):
用$注入器注册一个常量服务,如string,数字,数组,对象或函数。 像价值一样,不可能将其他服务注入到一个常量中。 但是与value不同的是, 一个常量可以被注入到一个模块configuration函数中(参见angular.Module),AngularJS装饰器不能覆盖它 。
因此,AngularJS constant
函数不能提供本领域术语通常理解的常数。
这就是说,对提供的对象施加的限制,以及通过$注入器提供的更早的可用性,清楚地表明这个名字被用于类比。
如果您想在AngularJS应用程序中使用一个实际的常量,那么您应该像在任何JavaScript程序中一样“提供”
export const π = 3.14159265;
在Angular 2中,同样的技术也是适用的。
Angular 2应用程序没有像AngularJS应用程序那样的configuration阶段。 此外,没有服务修饰器机制( AngularJS装饰器 ),但这并不特别令人惊讶,因为它们彼此之间有多么不同。
的例子
angular .module('mainApp.config', []) .constant('API_ENDPOINT', 'http://127.0.0.1:6666/api/');
是含糊不清的,并且稍微$provide.constant
,因为$provide.constant
被用来指定一个偶然也是常量的对象。 你可能已经写了
export const apiEndpoint = 'http://127.0.0.1:6666/api/';
所有人都可以改变。
现在,嘲笑常数的可testing性的论证被削弱了,因为它从字面上看并没有改变。
一个不嘲笑π。
当然,您的应用程序特定的语义可能是您的端点可能会更改,或者您的API可能具有非透明的故障转移机制,所以在某些情况下API端点更改是合理的。
但是在这种情况下,将其作为单个URL的string文字表示forms提供给constant
函数将不起作用。
一个更好的论点,可能与AngularJS $provide.constant
函数存在的原因更加一致的是,当AngularJS被引入时,JavaScript没有标准的模块概念。 在这种情况下,全局variables将被用来共享值,可变的或不可变的,并且使用全局variables是有问题的。
也就是说,通过一个框架提供这样的东西增加了与该框架的耦合。 它还将angular度特定的逻辑与可以在任何其他系统中工作的逻辑混合在一起。
这并不是说这是一个错误的或有害的方法,但个人而言,如果我想要一个Angular 2应用程序中的常量 ,我会写
export const π = 3.14159265;
就像我使用AngularJS一样。
事情变化越多…
Angular4的一个方法是在模块级定义一个常量:
const api_endpoint = 'http://127.0.0.1:6666/api/'; @NgModule({ declarations: [AppComponent], bootstrap: [AppComponent], providers: [ MessageService, {provide: 'API_ENDPOINT', useValue: api_endpoint} ] }) export class AppModule { }
然后,在您的服务:
import {Injectable, Inject} from '@angular/core'; @Injectable() export class MessageService { constructor(private http: Http, @Inject('API_ENDPOINT') private api_endpoint: string) { } getMessages(): Observable<Message[]> { return this.http.get(this.api_endpoint+'/messages') .map(response => response.json()) .map((messages: Object[]) => { return messages.map(message => this.parseData(message)); }); } private parseData(data): Message { return new Message(data); } }