首页 文章

在Angular 2中定义全局常量

提问于
浏览
195

在Angular 1.x中,您可以像这样定义常量:

angular.module('mainApp.config', [])
.constant('API_ENDPOINT', 'http://127.0.0.1:6666/api/')

Angular2(使用Typescript)中的等价物是什么?我只是不想在我的所有服务中一遍又一遍地重复API基本URL .

15 回答

  • 44

    只需使用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/'}), ...]);
    
  • 195

    Below changes works for me on Angular 2 final version:

    export class AppSettings {
       public static API_ENDPOINT='http://127.0.0.1:6666/api/';
    }
    

    And then in the service:

    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);
        }
    }
    
  • 30

    角度团队本身提供的配置解决方案可以在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)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
        }   
    }
    

    现在,您可以在不使用字符串名称的情况下将配置注入到任何地方,并使用您的接口进行静态检查 .

    您当然可以进一步分离界面和常量,以便能够在 生产环境 和开发中提供不同的值,例如:

  • 2

    在Angular2中,您具有以下provide定义,它允许您设置不同类型的依赖项:

    provide(token: any, {useClass, useValue, useExisting, useFactory, deps, multi}
    

    Comparing to Angular 1

    Angular1中的 app.service 等效于Angular2中的 useClass .

    Angular1中的 app.factory 等同于Angular2中的 useFactory .

    app.constantapp.value 已简化为 useValue ,约束较少 . 即不再有 config 块 .

    app.provider - Angular 2中没有等效词 .

    Examples

    要使用根注入器进行设置:

    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);
    
  • 7

    在Angular 4中,您可以使用环境类来保留所有全局变量 .

    默认情况下,您有environment.ts和environment.prod.ts .

    例如

    export const environment = {
      production: false,
      apiUrl: 'http://localhost:8000/api/'
    };
    

    然后是你的服务:

    import { environment } from '../../environments/environment';
    ...
    environment.apiUrl;
    
  • 0

    虽然使用带有字符串常量的AppSettings类作为ApiEndpoint的方法有效,但它并不理想,因为在单元测试时我们无法将这个真正的ApiEndpoint交换为其他值 .

    我们需要能够将这个api endpoints 注入到我们的服务中(考虑将服务注入到另一个服务中) . 我们也不需要为此创建一个完整的类,我们要做的就是在我们的ApiEndpoint服务中注入一个字符串 . 要完成excellent answer by pixelbits,以下是关于如何在Angular 2中完成的完整代码:

    首先,我们需要告诉Angular如何在我们的应用程序中请求它时的ApiEndpoint实例(将其视为注册依赖项):

    bootstrap(AppComponent, [
            HTTP_PROVIDERS,
            provide('ApiEndpoint', {useValue: 'http://127.0.0.1:6666/api/'})
    ]);
    

    然后在服务中我们 inject 这个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...
    }
    
  • 0

    已更新为Angular 4

    现在我们可以简单地使用环境文件,如果您的项目是通过angular-cli生成的,则angular提供默认值 .

    例如

    在您的环境文件夹中创建以下文件

    • environment.prod.ts

    • environment.qa.ts

    • environment.dev.ts

    并且每个文件都可以保存相关的代码更改,例如:

    • environment.prod.ts
    export const environment = {
         production: true,
         apiHost: 'https://api.somedomain.com/prod/v1/',
         CONSUMER_KEY: 'someReallyStupidTextWhichWeHumansCantRead', 
         codes: [ 'AB', 'AC', 'XYZ' ],
    };
    
    • environment.qa.ts
    export const environment = {
         production: false,
         apiHost: 'https://api.somedomain.com/qa/v1/',
         CONSUMER_KEY : 'someReallyStupidTextWhichWeHumansCantRead', 
         codes: [ 'AB', 'AC', 'XYZ' ],
    };
    
    • environment.dev.ts
    export const environment = {
         production: false,
         apiHost: 'https://api.somedomain.com/dev/v1/',
         CONSUMER_KEY : 'someReallyStupidTextWhichWeHumansCantRead', 
         codes: [ 'AB', 'AC', 'XYZ' ],
    };
    

    应用程序中的用例

    您可以将环境导入任何文件,例如服务 clientUtilServices.ts

    import {environment} from '../../environments/environment';

    getHostURL(): string {
        return environment.apiHost;
      }
    

    构建中的用例

    打开你的角度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 读取配置,就像你可以为 qadev 这样做

    ##较旧的回答

    在我的提供者中,我一直在做类似下面的事情:

    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)
      }
     }
    
  • 60

    这是我最近对此场景的体验:

    • @angular/cli: 1.0.0

    • node: 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@
    
      }
    

    结果是干净的,并且对于John Papa在本期中的最新评论感谢控制台上没有任何警告:

    https://github.com/angular/angular-cli/issues/2034

    关键是在不同的文件中实现接口 .

  • 30

    所有解决方案似乎都很复杂 . 我正在为这种情况寻找最简单的解决方案,我只想使用常量 . 常数很简单 . 有没有什么可以反对以下解决方案?

    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 );
        }
    
    }
    
  • 142

    如果您正在使用我推荐的Webpack,您可以为不同的环境设置常量 . 当您在每个环境的基础上具有不同的常量值时,这尤其有用 .

    您可能在 /config 目录下有多个webpack文件(例如,webpack.dev.js,webpack.prod.js等) . 然后,您将拥有每个文件中要遵循的一般模式以及组件中的示例用法 custom-typings.d.ts .

    webpack..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)
          }),
    

    custom-typings.d.ts

    declare var API_URL: string;
    declare var JWT_TOKEN_NAME: string;
    interface GlobalEnvironment {
      API_URL: string;
      JWT_TOKEN_NAME: string;
    }
    

    Component

    export class HomeComponent implements OnInit {
      api_url:string = API_URL;
      authToken: string = "Bearer " + localStorage.getItem(JWT_TOKEN_NAME)});
    }
    
  • 12

    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);
        }
    }
    
  • 3

    使用在构建期间生成的属性文件非常简单 . 这是Angular CLI使用的方法 . 为每个环境定义属性文件,并在构建期间使用命令来确定将哪个文件复制到您的应用程序 . 然后只需导入要使用的属性文件 .

    https://github.com/angular/angular-cli#build-targets-and-environment-files

  • 27

    AngularJS的 module.constant 没有在标准意义上定义常量 .

    虽然它作为提供者注册机制独立存在,但最好在相关的 module.value$provide.value )函数的上下文中理解 . 官方文档清楚地说明了用例:

    使用$ injector注册值服务,例如字符串,数字,数组,对象或函数 . 这是注册服务的简称,其服务提供者的$ get属性是一个不带参数的工厂函数并返回值服务 . 这也意味着无法将其他服务注入 Value 服务 .

    将此与 module.constant$provide.constant )的文档进行比较,该文档还清楚地说明了用例(强调我的):

    使用$ injector注册常量服务,例如字符串,数字,数组,对象或函数 . 与值一样,不可能将其他服务注入常量 . 但与值不同,常量可以注入模块配置函数(请参阅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/';
    

    对于所有人都可以改变 .

    现在,可测性的论证, Mock 常数,因为它实际上没有改变而减少了 .

    一个人不会嘲笑π .

    当然,您的应用程序特定语义可能是您的 endpoints 可能更改,或者您的API可能具有不透明的故障转移机制,因此在某些情况下API endpoints 更改是合理的 .

    但在这种情况下,将它作为单个URL的字符串文字表示提供给 constant 函数是行不通的 .

    一个更好的论点,并且可能更符合AngularJS $provide.constant 函数存在的原因是,当AngularJS被引入时,JavaScript没有标准的模块概念 . 在这种情况下,全局变量将用于共享值,可变或不可变,并且使用全局变量是有问题的 .

    也就是说,通过框架提供这样的东西会增加与该框架的耦合 . 它还将Angular特定逻辑与可在任何其他系统中工作的逻辑混合使用 .

    这并不是说这是一种错误或有害的方法,但就个人而言,如果我想在Angular 2应用程序中使用常量,我会写

    export const π = 3.14159265;
    

    就像我一样,我使用的是AngularJS .

    事情变化越多......

  • -1

    在Angular 2中创建应用程序范围常量的最佳方法是使用environment.ts文件 . 声明这些常数的优点是你可以改变它们根据环境,因为每个环境可以有不同的环境文件 .

  • 4

    您可以为全局变量创建一个类,然后像这样导出这个类:

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

    您可以在 constructorngOnInit(){} 或任何预定义方法中使用它 .

相关问题