グローバル定数を定義する


258

Angular 1.xでは、次のようにして定数を定義できます。

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

Angular(TypeScriptを使用)で同等のものは何ですか?

すべてのサービスでAPIベースURLを何度も繰り返したくありません。

回答:


265

以下の変更点は、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);
    }
}

yout AppSettingsクラスは抽象的で、API_ENDPOINTメンバーはである必要がありますreadonly
フィリップジョセフィ

164

Angularチーム自体が提供する構成の解決策は、こちらにあります

ここにすべての関連コードがあります:

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)private config){}を実行すると機能します。これについては、blog.thoughtram.io / angular / 2016/05/23 / …で言及されています が、理由はわかりません。
ムクス2016年

インポートやエクスポートのキーワードなどを見逃していたと思います。インターフェイスで使用しているため、明示的に静的に型指定することが非常に重要です。おそらく、ここで正確な例外を提供する必要があります。
Ilya Chernomordik 2016年

46
これらのソリューションはどれも、角張ったチームが推奨するアプローチでさえエレガントに見えます。Angular 2で定数を作成するのが面倒なプロセスなのはなぜですか?Angular1がどのようにシームレスに作成されたのかわかりませんか?なぜすべての混乱ですか?
KhoPhi 2017年

31
-角度V4でヒットこの回答OpaqueTokenがInjectionTokenのために、「非推奨」されていることを他の誰のためのblog.thoughtram.io/angular/2016/05/23/...
mtpultz

3
でしょうが、それはにステップ1からコードを置くために意味をなすenvironment.tsenvironment.prod.tsあなたは環境ごとに異なる定数を持つことができるように?@IlyaChernomordikは彼の回答の最後の段落でこれについて言及し始めました。
Robert Bernstein

64

Angular2では、次のように定義を定義することで、さまざまな種類の依存関係をセットアップできます。

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

Angular 1と​​比較する

app.serviceAngular1の場合はAngular2の場合と同じuseClassです。

app.factory Angular1では useFactoryです。

app.constantapp.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このファイルはどのように見えますか?
AndreFeijo 2016年

NodeJSなどのサーバーサイドJavaScriptを検討しましたか?
pixelbits 2016年

5
サービスに挿入する方法がわかりませんでした。外部ファイルを使用しているので、エクスポートする必要がありますか?
AndreFeijo 2016年

ビルド構成プロセスの一部にします。つまり、環境に基づいて、さまざまなファイルをまとめてコンパイル/パッケージ化してから、デプロイします。これはすべて、適切なモジュールを使用してNodeJSで実行できます。
pixelbits 2016年

1
残念ながら、NodeJSはオプションではありません。
AndreFeijo 2016年

59

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 }。これなしで実行時エラーが発生しました。
daleyjem

@daleyjemそれを注入しようとしていたからです。このアプローチはインジェクターを使用しません
Aluan Haddad

このような定数の作成は最も簡単なものです。DIが失われ、それによってtestability / mockValueが失われるという反論は、しばらくの間過剰に宣伝されていると思います。典型的なアプリでは、テスト性を気にすることなく、(RxJS)のような非常に多くの非DIコンポーネントを使用します。
アミテッシュ

54

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

6
これは定数のようには機能しません。定数の値は常に同じです。あなたの場合、あなたのAPI_ENDPOINT値はいつでも上書きすることができます。場合はthis.ConstantService.API_ENDPOINT = 'blah blah'、あなたのいわゆる「定数」の後にクラスいつに宣言されてからインポートされconstant-service、API_ENDPOINTの新しい値は次のようになります'blah blah'。ソリューションは、定数を使用するのではなく、サービスを使用して変数にアクセスする方法を示しています。
デブナー2017年

1
@Devnerだけ読み取り専用それらを作るreadonly API_ENDPOINT :String;
Flavien Volken

@Anjum Angularがenvファイルを選択する方法。アプリの起動時に環境名を渡す必要がありますか?
notionquest '19

@notionquestはい、あなたはそれを渡すことができますng build --env=prod
Anjum ....

31

ApiEndpointとして文字列定数を含むAppSettingsクラスを使用するアプローチは機能しますが、単体テスト時にこの実際のApiEndpointを他のいくつかの値と交換できないため、理想的ではありません。

このAPIエンドポイントをサービスに挿入できる必要があります(サービスを別のサービスに挿入することを考えてください)。また、このためにクラス全体を作成する必要はありません。ApiEndpointであるサービスに文字列を挿入するだけです。pixelbitsによる優れた回答を完了するために、Angular 2でどのように実行できるかに関する完全なコードを次に示します。

まず、アプリでApiEndpointのインスタンスを要求するときに、Angularにインスタンスを提供する方法を指示する必要があります(依存関係の登録と考えてください)。

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


そしてサービスで、我々は注入サービスコンストラクタにこのApiEndpointを、角は上記の当社の登録に基づいて、私たちのためにそれを提供します:

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
Ilya Chernomordik

1
このコードは正確ではなくなりました。これを実装すると、AppComponentにApiEndpointが見つかりません。
WilliamX 2017

OK、私は一人じゃない。これが壊れたバージョンを知っていますか?グローバルオブジェクトに値を定義してからそれらを提供する必要がない代替方法はありますか?
Jens Bodal、

29

これは、このシナリオでの最近の経験です。

  • @ 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@

  }

結果はクリーンで、コンソールに警告はありません。この問題に関するJohn Papaの最近のコメントに感謝します。

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

キーは、インターフェイスの別のファイルに実装されていました。


stackoverflow.com/a/43193574/3092596も参照してください。これは基本的に同じですが、プロバイダーではなく注入可能なモジュールを作成します
goredwards

19

すべてのソリューションは複雑なようです。この場合の最も簡単な解決策を探しており、定数を使用したいだけです。定数は単純です。次の解決策に反することはありますか?

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依存関係注入システムに定数を取得して、スコープをクリーン、スタブ可能、またはモック可能に保つことです。
Joel Hernandez

11

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
Sasxa

@Sasxa同意しますが、単体テストなどには適しています。完全な答えを提供しようとしています。
SnareChops

1
あなたが使用することができ@Andreas constyest
SnareChops

この作業のスタックブリッツを提供してください。ブートストラップメソッドでサービスを提供する例をたくさん見てきましたが、まだ十分に機能する例を見つけられていません。おそらく、angularの最近のバージョンで何かが変更されています。
Jens Bodal

4

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

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の1つのアプローチは、モジュールレベルで定数を定義することです。

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

グローバル定数を定義する別の方法があります。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
  }
}

アプリフォルダーで、フォルダー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ファイルの値を簡単に変更できます。私はこの方法を常にエラー/警告メッセージに使用します。


0

AngularJSの module.constantは標準的な意味で定数を定義していません。

プロバイダーの登録メカニズムとして単独で使用されますが、関連するmodule.value$provide.value)関数のコンテキストで理解するのが最適です。公式文書はユースケースを明確に述べています:

文字列、数値、配列、オブジェクト、関数などの値サービスを$ injectorに登録します。これは、プロバイダーの$ getプロパティが引数を取らずに値サービスを返すファクトリー関数であるサービスを登録するための略語です。つまり、他のサービスをバリューサービスに挿入することはできません。

これを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/';

すべてが変わる可能性があるからです。

定数をあざける、テスト容易性の議論は、文字通り変化しないので減少します。

πをモックしない。

もちろん、アプリケーション固有のセマンティクスは、エンドポイントが変更される可能性があるか、APIが非透過的なフェイルオーバーメカニズムを備えている可能性があるため、APIエンドポイントが特定の状況で変更されるのは妥当です。

しかし、その場合、constant関数への単一のURLの文字列リテラル表現として提供しても機能しませんでした。

AngularJS $provide.constant関数が導入されたとき、JavaScriptには標準モジュールの概念がなかったというのが、より良い議論であり、AngularJS 関数が存在する理由とより整合している可能性があります。その場合、グローバルは値を共有するために使用され、可変または不変であり、グローバルの使用には問題があります。

そうは言っても、フレームワークを通じてこのようなものを提供すると、そのフレームワークへの結合が増加します。また、Angular固有のロジックと他のシステムで機能するロジックを組み合わせます。

これは、それが間違っているまたは有害なアプローチであると言っているわけではありませんが、個人的には、Angular 2アプリケーションで定数が必要な場合は、

export const π = 3.14159265;

AngularJSを使用していたのと同じように。

変化するほど...


0

Angular 2でアプリケーション全体の定数を作成する最良の方法は、environment.tsファイルを使用することです。このような定数を宣言する利点は、環境ごとに異なる環境ファイルが存在する可能性があるため、環境に応じて定数を変更できることです。


アプリケーションを一度ビルドしてから複数の環境にデプロイする場合、これは機能しません。
Jens Bodal、

-1

グローバル変数のクラスを作成し、このクラスを次のようにエクスポートできます。

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(){}、または任意の事前定義メソッドで使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.