在 Angular 1.x 中,您可以像这样定义常量:
angular.module('mainApp.config', [])
.constant('API_ENDPOINT', 'http://127.0.0.1:6666/api/')
Angular(使用 TypeScript)中的等价物是什么?
我只是不想在我的所有服务中一遍又一遍地重复 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 团队本身提供的配置解决方案 here。
以下是所有相关代码:
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)你的.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
}
}
现在,您可以在任何地方注入配置,而无需使用字符串名称并使用您的接口进行静态检查。
您当然可以进一步分离接口和常量,以便能够在生产和开发中提供不同的值,例如
environment.ts
和 environment.prod.ts
以便您可以在每个环境中使用不同的常量是否有意义? @IlyaChernomordik 在他回答的最后一段中开始提到这一点。
为 Angular 4+ 更新
如果您的项目是通过 angular-cli 生成的,现在我们可以简单地使用 angular 提供默认值的环境文件。
例如
在您的环境文件夹中创建以下文件
环境.prod.ts
环境.qa.ts
环境.dev.ts
每个文件都可以保存相关的代码更改,例如:
environment.prod.ts export const environment = { production: true, apiHost: 'https://api.somedomain.com/prod/v1/', CONSUMER_KEY: 'someReallyStupidTextWhichWeHumansCantRead', 代码: ['AB', 'AC', 'XYZ' ], };
environment.qa.ts export const environment = { production: false, apiHost: 'https://api.somedomain.com/qa/v1/', CONSUMER_KEY : 'someReallyStupidTextWhichWeHumansCantRead', 代码: ['AB', 'AC', 'XYZ' ], };
environment.dev.ts export const environment = { production: false, apiHost: 'https://api.somedomain.com/dev/v1/', CONSUMER_KEY : 'someReallyStupidTextWhichWeHumansCantRead', 代码: ['AB', 'AC', 'XYZ' ], };
应用中的用例
您可以将环境导入任何文件,例如服务 clientUtilServices.ts
import {environment} from '../../environments/environment';
getHostURL(): string {
return environment.apiHost;
}
构建中的用例
打开您的 Angular cli 文件 .angular-cli.json
并在 "apps": [{...}]
内添加以下代码
"apps":[{
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts",
"qa": "environments/environment.qa.ts",
}
}
]
如果您想为生产构建,运行 ng build --env=prod
它将从 environment.prod.ts
读取配置,您可以为 qa
或 dev
执行相同的方式
##老答案
在我的提供商中,我一直在做类似下面的事情:
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)
}
}
API_ENDPOINT
值可以在任何时间点被覆盖。如果在从 constant-service
导入所谓的“常量”之后的任何时候在类中声明 this.ConstantService.API_ENDPOINT = 'blah blah'
,那么 API_ENDPOINT 的新值将是 'blah blah'
。您的解决方案只是展示了如何使用服务而不是使用常量来访问变量。
readonly API_ENDPOINT :String;
ng build --env=prod
在 Angular2 中,您有以下 provide 定义,它允许您设置不同类型的依赖项:
provide(token: any, {useClass, useValue, useExisting, useFactory, deps, multi}
与 Angular 1 相比
Angular1 中的 app.service
等效于 Angular2 中的 useClass
。
Angular1 中的 app.factory
等效于 Angular2 中的 useFactory
。
app.constant
和 app.value
已简化为具有较少约束的 useValue
。即不再有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);
在 Angular 4 中,您可以使用环境类来保留所有全局变量。
默认情况下,您有 environment.ts 和 environment.prod.ts。
例如
export const environment = {
production: false,
apiUrl: 'http://localhost:8000/api/'
};
然后在您的服务上:
import { environment } from '../../environments/environment';
...
environment.apiUrl;
const
,则可能必须在应用模块的提供程序数组中“提供”它:{ provide: 'ConstName', useValue: ConstName }
。如果没有这个,我会遇到运行时错误。
虽然使用带有字符串常量的 AppSettings 类作为 ApiEndpoint 的方法有效,但它并不理想,因为在单元测试时我们无法将这个真实的 ApiEndpoint 交换为其他一些值。
我们需要能够将这个 api 端点注入到我们的服务中(想想将一个服务注入到另一个服务中)。我们也不需要为此创建一个完整的类,我们要做的就是将一个字符串注入到我们的服务中,作为我们的 ApiEndpoint。要完成 excellent answer by pixelbits,下面是关于如何在 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...
}
这是我最近对这种情况的体验:
@角/cli:1.0.0
节点:6.10.2
@角/核心: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@
}
结果很干净,控制台上没有警告感谢 John Papa 在此问题上的最新评论:
https://github.com/angular/angular-cli/issues/2034
关键是在不同的文件中实现接口。
所有解决方案似乎都很复杂。我正在为这种情况寻找最简单的解决方案,我只想使用常量。常数很简单。有什么反对以下解决方案的吗?
app.const.ts
'use strict';
export const dist = '../path/to/dist/';
应用服务.ts
import * as AppConst from '../app.const';
@Injectable()
export class AppService {
constructor (
) {
console.log('dist path', AppConst.dist );
}
}
只需使用 Typescript 常量
export var API_ENDPOINT = 'http://127.0.0.1:6666/api/';
您可以在依赖注入器中使用它
bootstrap(AppComponent, [provide(API_ENDPOINT, {useValue: 'http://127.0.0.1:6666/api/'}), ...]);
const
yest
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);
}
}
如果您使用我推荐的 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)
}),
自定义类型.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)});
}
我有另一种定义全局常量的方法。因为如果我们在 ts 文件中定义,如果在生产模式下构建,则不容易找到常量来更改值。
export class SettingService {
constructor(private http: HttpClient) {
}
public getJSON(file): Observable<any> {
return this.http.get("./assets/configs/" + file + ".json");
}
public getSetting(){
// use setting here
}
}
在 app 文件夹中,我添加文件夹 configs/setting.json
setting.json 中的内容
{
"baseUrl": "http://localhost:52555"
}
在应用模块中添加 APP_INITIALIZER
{
provide: APP_INITIALIZER,
useFactory: (setting: SettingService) => function() {return setting.getSetting()},
deps: [SettingService],
multi: true
}
通过这种方式,我可以更轻松地更改 json 文件中的值。我也将这种方式用于持续的错误/警告消息。
使用在构建期间生成的属性文件既简单又容易。这是 Angular CLI 使用的方法。为每个环境定义一个属性文件,并在构建期间使用命令来确定将哪个文件复制到您的应用程序。然后只需导入要使用的属性文件。
https://github.com/angular/angular-cli#build-targets-and-environment-files
在阅读了该线程的所有答案以及其他一些答案之后,我想提供我这些天正在使用的解决方案。
首先,我必须为环境添加一个类。有了这个,我实现了我的属性的数据类型,所以它很容易使用。此外,我可以将默认数据绑定到我的环境,这样我就可以在所有环境之间共享公共数据。有时我们有一些在所有环境中具有相同值的变量(例如站点名称),我们不想每次都更改为所有环境。
// environments\ienvironments.ts
export class IEnvironment implements IEnvironmentParams {
public production: boolean;
public basicURL: string = 'https://www.someawesomedomain.com';
public siteName: string = 'My awesome site';
constructor(params: IEnvironmentParams) {
this.production = params.production ?? false;
this.basicURL = params.basicURL ?? this.basicURL;
this.siteName = params.siteName ?? this.siteName;
}
}
export interface IEnvironmentParams {
production: boolean;
basicURL?: string;
siteName?: string;
}
请注意,我使用 IEnvironmentParams 来简化环境的创建,这样我可以传递对象而不会弄乱构造函数参数并避免参数顺序问题,还可以使用 ??
运算符提供所需的默认值功能。
// environments\environment.prod.ts
import {IEnvironment, IEnvironmentParams} from "./ienvironment";
const params: IEnvironmentParams = {
production: true
};
export const environment: IEnvironment = new IEnvironment(params);
// environments\environment.ts
import {IEnvironment, IEnvironmentParams} from "./ienvironment";
const params: IEnvironmentParams = {
production: false
};
export const environment: IEnvironment = new IEnvironment(params);
用法示例
import {environment} from "../environments/environment";
// app-routing.module.ts
const routes: Routes = [
{
path: '', component: HomeComponent,
data: {
title: `${environment.siteName} | Home page title!`,
description: 'some page description',
}
}
];
https://i.stack.imgur.com/7ZpU5.png
// home.component.ts
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent {
constructor() {
console.log(`home component constructor - showing evironment.siteName - ${environment.siteName}`);
}
}
你可以在任何你想要的地方使用它,类、服务、指令、组件等。
对于那些想在构建后替换值的人。你能行的。这有点棘手,但是当您构建 Angular 应用程序时,环境数据会导出到 main.js
,请看下一个屏幕截图。
https://i.stack.imgur.com/Ge0Rl.png
只需在任何 IDE 中打开文件并查找 environment
,然后只需替换数据即可。
关于 Angular 通用项目。构建 Angular Universal 项目时,它将导出 2 main.js
一个用于服务器,一个用于浏览器,因此您必须同时更改两者。
你可以为你的全局变量创建一个类,然后像这样导出这个类:
export class CONSTANT {
public static message2 = [
{ "NAME_REQUIRED": "Name is required" }
]
public static message = {
"NAME_REQUIRED": "Name is required",
}
}
创建并导出 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
) 函数的上下文中理解。官方文档清楚地说明了用例:
使用 $injector 注册一个值服务,例如字符串、数字、数组、对象或函数。这是注册服务的缩写,其中提供者的 $get 属性是一个工厂函数,不接受任何参数并返回值服务。这也意味着不可能将其他服务注入到价值服务中。
将此与 module.constant
($provide.constant
) 的文档进行比较,该文档也清楚地说明了用例(强调我的):
使用 $injector 注册一个常量服务,例如字符串、数字、数组、对象或函数。像值一样,不可能将其他服务注入常量。但与 value 不同的是,常量可以注入到模块配置函数中(参见 angular.Module),并且不能被 AngularJS 装饰器覆盖。
因此,AngularJS constant
函数不提供该领域术语的通常理解含义中的常量。
也就是说,对提供的对象的限制,以及通过 $injector 更早的可用性,清楚地表明该名称是通过类比使用的。
如果你想要一个 AngularJS 应用程序中的实际常量,你可以像在任何 JavaScript 程序中一样“提供”一个常量
export const π = 3.14159265;
在 Angular 2 中,同样的技术也适用。
Angular 2 应用程序没有与 AngularJS 应用程序相同的配置阶段。此外,没有服务装饰器机制 (AngularJS Decorator),但这并不奇怪,因为它们彼此之间有多么不同。
的例子
angular
.module('mainApp.config', [])
.constant('API_ENDPOINT', 'http://127.0.0.1:6666/api/');
是模糊的任意且有点令人反感,因为 $provide.constant
被用来指定一个对象,顺便说一下也是一个常量。你还不如写
export const apiEndpoint = 'http://127.0.0.1:6666/api/';
因为任何一个都可以改变。
现在,嘲笑常数的可测试性论据被削弱了,因为它实际上并没有改变。
一个人不会嘲笑 π。
当然,您的应用程序特定语义可能是您的端点可能会更改,或者您的 API 可能具有不透明的故障转移机制,因此在某些情况下更改 API 端点是合理的。
但在这种情况下,将其作为单个 URL 的字符串文字表示形式提供给 constant
函数是行不通的。
一个更好的论点,并且可能更符合 AngularJS $provide.constant
函数存在的原因是,当 AngularJS 被引入时,JavaScript 没有 标准 模块概念。在这种情况下,全局变量将用于共享可变或不可变的值,并且使用全局变量是有问题的。
也就是说,通过框架提供类似的东西会增加与该框架的耦合。它还将 Angular 特定逻辑与适用于任何其他系统的逻辑混合在一起。
这并不是说这是一种错误或有害的方法,但就个人而言,如果我想在 Angular 2 应用程序中使用常量,我会写
export const π = 3.14159265;
就像我使用 AngularJS 一样。
变化越多...
在 Angular 2 中创建应用程序范围常量的最佳方法是使用 environment.ts 文件。声明此类常量的优点是您可以根据环境改变它们,因为每个环境可以有不同的环境文件。
不定期副业成功案例分享
AppSettings
类应该是抽象的,API_ENDPOINT
成员应该是readonly
。