Определите глобальные константы в Angular 2

195

В Angular 1.x вы можете определить константы следующим образом:

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

Что было бы эквивалентно в Angular2 (с Typescript)? Я просто не хочу повторять URL-адрес базы API снова и снова во всех моих сервисах.

Теги:
angular

15 ответов

189
Лучший ответ

Ниже меняются изменения для меня на 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);
    }
}
  • 25
    Он не включает DI и, следовательно, не является идеальным аналогом constant .
  • 3
    @estus DI не всегда идеален, хотя. Он не будет работать в любом произвольном месте, из которого вы хотите получить доступ к константе, а только из конструкторов. Что, если вы хотите предоставить константу декоратору, например?
Показать ещё 2 комментария
140

Решение для конфигурации, предоставляемой самой угловой командой, можно найти здесь.

Вот весь соответствующий код:

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

Теперь вы можете вставлять конфиг везде без использования имен строк и с использованием вашего интерфейса для статических проверок.

Разумеется, вы можете разделить интерфейс и константу дальше, чтобы иметь возможность поставлять различные значения в производстве и разработке, например

  • 3
    Это работает только тогда, когда я не указываю тип в конструкторе службы. Так что это работает, когда я делаю конструктор (@Inject (APP_CONFIG) частная конфигурация) {} Здесь есть упоминание об этом: blog.thoughtram.io/angular/2016/05/23/… но не почему.
  • 0
    Я предполагаю, что вы пропустили какое-то ключевое слово import или export или что-то в этом роде, поскольку я использую его с интерфейсом, и, как вы говорите, очень важно, чтобы он был явно статически типизирован. Может быть, вам нужно предоставить точное исключение здесь.
Показать ещё 8 комментариев
59

В Angular2 у вас есть следующее обеспечить определение, которое позволяет вам устанавливать различные зависимости:

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

Сравнение с Angular 1

app.service в Angular1 эквивалентно useClass в Angular2.

app.factory в Angular1 эквивалентно useFactory в Angular2.

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);
  • 2
    Я действительно хотел бы, чтобы мои настройки были во внешнем файле, например: settings.ts Как бы выглядел этот файл?
  • 0
    Рассматривали ли вы серверный JavaScript, такой как NodeJS?
Показать ещё 3 комментария
41

В 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
    Если вы пытаетесь получить доступ к const внутри службы, вам, возможно, придется «предоставить» его в массиве провайдеров вашего модуля приложения: { provide: 'ConstName', useValue: ConstName } . Я получил ошибку во время выполнения без этого.
  • 0
    @daleyjem это потому что ты пытался это сделать. Этот подход не использует инжектор
Показать ещё 1 комментарий
31

Хотя подход с наличием класса AppSettings со строковой константой как ApiEndpoint работает, он не идеален, так как мы не смогли бы поменять эту реальную ApiEndpoint на некоторые другие значения во время модульного тестирования.

Нам нужно иметь возможность вводить эти конечные точки api в наши сервисы (подумайте о том, чтобы вводить услугу в другую службу). Для этого нам также не нужно создавать целый класс, все, что мы хотим сделать, - это ввести строку в наши сервисы, являющиеся нашей ApiEndpoint. Чтобы закончить отличный ответ на 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...
}
  • 1
    В настоящее время есть «официальный» способ выполнения рекомендаций угловой командой в их уроке. Я добавил ответ ниже: ( stackoverflow.com/a/40287063/1671558 )
  • 1
    этот код больше не является точным, реализация этого приведет к тому, что ApiEndpoint не найден в AppComponent.
Показать ещё 1 комментарий
27

Это мой недавний опыт в этом сценарии:

  • @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 Key: ", this.config.STORE_KEY);
    //> This is the App Key:  l@_list@

  }

Результат чист и нет предупреждений на консоли, благодаря недавнему комментарию Джона Папа в этом выпуске:

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

Ключ был реализован в другом файле интерфейса.

  • 0
    см. также stackoverflow.com/a/43193574/3092596 - который в основном такой же, но создает инъекционные модули, а не провайдеров
27

Обновлено для Angular 4+

Теперь мы можем просто использовать файл окружения, который по умолчанию задает угловое значение, если ваш проект генерируется с помощью углового кли.

например

В папке среды создайте следующие файлы

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

Вариант использования в сборке

Откройте свой угловой файл .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)
  }
 }
  • 4
    Это не работает как константа. Значение константы всегда одинаково. В вашем случае ваше значение API_ENDPOINT может быть API_ENDPOINT в любой момент времени. Если this.ConstantService.API_ENDPOINT = 'blah blah' объявлен в классе в любое время после того, как ваша так называемая "константа" импортирована из constant-service , новое значение API_ENDPOINT будет 'blah blah' . Ваше решение просто показывает, как получить доступ к переменной с помощью службы, а не с помощью константы.
  • 1
    @Devner просто делает их доступными только для readonly API_ENDPOINT :String;
Показать ещё 2 комментария
11

Все решения кажутся сложными. Я ищу простейшее решение для этого случая, и я просто хочу использовать константы. Константы просты. Есть ли что-нибудь, что говорит против следующего решения?

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

}
  • 2
    Что ж, вы используете переменные, выходящие за рамки сервиса, так что вы можете просто использовать глобальные переменные окна. То, что мы пытаемся сделать, - это ввести константы в систему внедрения зависимостей Angular4, чтобы мы могли поддерживать область действия чистой, стабильной или поддельной.
7

Просто используйте константу 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/'}), ...]);
  • 1
    Зачем вводить это? Думаю, в этом нет необходимости ... вы можете использовать его, как только импортируете. @SnareChops
  • 0
    @Sasxa Я согласен, хотя это может быть полезно для модульного тестирования и тому подобное. Просто пытаюсь дать полный ответ.
Показать ещё 3 комментария
4

Если вы используете Webpack, который я рекомендую, вы можете настроить константы для разных сред. Это особенно ценно, когда у вас есть разные постоянные значения для каждой среды.

Вероятно, в вашем каталоге /config (например, 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)
      }),

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

Один подход для 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

Использование файла свойств, созданного во время сборки, является простым и легким. Это подход, который использует CLI Angular. Определите файл свойств для каждой среды и используйте команду во время сборки, чтобы определить, какой файл будет скопирован в ваше приложение. Затем просто импортируйте файл свойств для использования.

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

1

AngularJS module.constant не определяет константу в стандартном смысле.

В то время как он сам по себе является механизмом регистрации провайдера, его лучше понимать в контексте связанной функции module.value ($provide.value). В официальной документации четко указывается пример использования:

Зарегистрируйте службу значений с помощью $injector, например строки, числа, массива, объекта или функции. Это коротко для регистрации службы, где его свойство $get является функцией factory, которая не принимает аргументов и возвращает службу значений. Это также означает, что нельзя вводить другие службы в службу стоимости.

Сравните это с документацией для module.constant ($provide.constant), в которой также четко указывается прецедент (основное внимание):

Зарегистрируйте постоянную службу с помощью инжектора $, например строки, числа, массива, объекта или функции. Как и значение, невозможно добавить другие службы в константу. Но в отличие от значения константа может быть введена в конфигурационную функцию модуля (см. 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.

Чем больше изменений...

0

Лучший способ создать широкие константы приложения в Angular 2 - это использовать файлы environment.ts. Преимущество объявления таких констант состоит в том, что вы можете изменять их в зависимости от среды, поскольку для каждой среды может быть другой файл среды.

  • 0
    Это не сработает, если вы собираетесь создать приложение один раз, а затем развернуть его в нескольких средах.
0

Вы можете создать класс для своей глобальной переменной, а затем экспортировать этот класс следующим образом:

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(){}, либо в любых предопределенных методах.

  • 0
    Совсем не обязательно

Ещё вопросы

Сообщество Overcoder
Наверх
Меню