App.settings - Угловой путь?

Я хочу добавить раздел " App Settings " в свое приложение, где он будет содержать некоторые константы и предварительно определенные значения.

Я уже читал этот ответ, который использует OpaqueToken но в Angular он устарел. Эта статья объясняет различия, но она не дала полный пример, и мои попытки оказались безуспешными.

Вот что я попробовал (я не знаю, правильно ли это):

//ServiceAppSettings.ts

import {InjectionToken, OpaqueToken} from "@angular/core";

const CONFIG = {
  apiUrl: 'http://my.api.com',
  theme: 'suicid-squad',
  title: 'My awesome app'
};
const FEATURE_ENABLED = true;
const API_URL = new InjectionToken<string>('apiUrl');

И это компонент, где я хочу использовать эти констант:

//MainPage.ts

import {...} from '@angular/core'
import {ServiceTest} from "./ServiceTest"

@Component({
  selector: 'my-app',
  template: '
   <span>Hi</span>
  ' ,  providers: [
    {
      provide: ServiceTest,
      useFactory: ( apiUrl) => {
        // create data service
      },
      deps: [

        new Inject(API_URL)
      ]
    }
  ]
})
export class MainPage {


}

Но это не работает, и я получаю ошибки.

Вопрос:

Как я могу использовать "app.settings" значения угловым способом?

plunker

NB Конечно, я могу создать сервис Injectable и поместить его в провайдер NgModule, но, как я уже сказал, я хочу сделать это с помощью InjectionToken, Angular.

Ответ 1

Я выяснил, как это сделать с помощью InjectionTokens (см. пример ниже), и если ваш проект был создан с использованием Angular CLI, вы можете использовать файлы окружения, найденные в /environments для статического application wide settings, как конечная точка API, но в зависимости от ваших требований к проекту вы, скорее всего, закончите использование обоих, поскольку файлы окружения являются просто объектными литералами, тогда как инъецируемая конфигурация с использованием InjectionToken может использовать переменные окружения, и поскольку в классе может быть применена логика для ее настройки на основе других факторы в приложении, такие как начальные данные запроса HTTP, субдомен и т.д.

Пример токенов инъекции

/app/app-config.module.ts

import { NgModule, InjectionToken } from '@angular/core';
import { environment } from '../environments/environment';

export let APP_CONFIG = new InjectionToken<AppConfig>('app.config');

export class AppConfig {
  apiEndpoint: string;
}

export const APP_DI_CONFIG: AppConfig = {
  apiEndpoint: environment.apiEndpoint
};

@NgModule({
  providers: [{
    provide: APP_CONFIG,
    useValue: APP_DI_CONFIG
  }]
})
export class AppConfigModule { }

/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppConfigModule } from './app-config.module';

@NgModule({
  declarations: [
    // ...
  ],
  imports: [
    // ...
    AppConfigModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Теперь вы можете просто вставить его в любой компонент, сервис и т.д.:

/app/core/auth.service.ts

import { Injectable, Inject } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

import { APP_CONFIG, AppConfig } from '../app-config.module';
import { AuthHttp } from 'angular2-jwt';

@Injectable()
export class AuthService {

  constructor(
    private http: Http,
    private router: Router,
    private authHttp: AuthHttp,
    @Inject(APP_CONFIG) private config: AppConfig
  ) { }

  /**
   * Logs a user into the application.
   * @param payload
   */
  public login(payload: { username: string, password: string }) {
    return this.http
      .post(`${this.config.apiEndpoint}/login`, payload)
      .map((response: Response) => {
        const token = response.json().token;
        sessionStorage.setItem('token', token); // TODO: can this be done else where? interceptor
        return this.handleResponse(response); // TODO:  unset token shouldn't return the token to login
      })
      .catch(this.handleError);
  }

  // ...
}

Затем вы можете также ввести проверку конфигурации с помощью экспортированного AppConfig.

Ответ 2

Если вы используете , есть еще один вариант:

Angular CLI предоставляет файлы окружения в src/environments (по умолчанию это environment.ts (dev) и environment.prod.ts (production)).

Обратите внимание, что вам нужно предоставить параметры конфигурации во всех файлах environment.*, Например,

environment.ts:

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

environment.prod.ts:

export const environment = {
  production: true,
  apiEndpoint: '__your_production_server__'
};

и использовать их в своем сервисе (правильный файл среды выбирается автоматически):

api.service.ts

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

@Injectable()
export class ApiService {     

  public apiRequest(): Observable<MyObject[]> {
    const path = environment.apiEndpoint + '/objects';
    // ...
  }

// ...
}

Узнайте больше о прикладных средах на Github (Angular CLI версии 6) или в официальном руководстве по Angular (версии 7).

Ответ 3

Вот мое решение, загружает из.json, чтобы позволить изменения без перестроения

import { Injectable, Inject } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Location } from '@angular/common';

@Injectable()
export class ConfigService {

    private config: any;

    constructor(private location: Location, private http: Http) {
    }

    async apiUrl(): Promise<string> {
        let conf = await this.getConfig();
        return Promise.resolve(conf.apiUrl);
    }

    private async getConfig(): Promise<any> {
        if (!this.config) {
            this.config = (await this.http.get(this.location.prepareExternalUrl('/assets/config.json')).toPromise()).json();
        }
        return Promise.resolve(this.config);
    }
}

и config.json

{
    "apiUrl": "http://localhost:3000/api"
}

Ответ 4

Не рекомендуется использовать файлы environment.*.ts для настройки URL-адреса API. Кажется, что вы должны, потому что это упоминает рабочую "среду".

Используя это на самом деле конфигурации во время компиляции. Если вы хотите изменить URL-адрес API, вам нужно будет перестроить его. Это то, что вы не хотите делать... просто спросите в вашем дружественном отделе контроля качества :)

Что вам нужно, это конфигурация во время выполнения, то есть приложение загружает свою конфигурацию при запуске.

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

Для реализации конфигурации во время выполнения:

  1. Добавьте файл конфигурации JSON в папку /src/assets/ (чтобы он копировался при сборке)
  2. Создать AppConfigService для загрузки и распространения конфигурации
  3. Загрузите конфигурацию, используя APP_INITIALISER

1. Добавьте файл конфигурации в /src/assets

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

{
  "apiBaseUrl": "https://development.local/apiUrl"
}

2. Создать AppConfigService

Это сервис, который будет добавлен всякий раз, когда вам нужно значение конфигурации:

@Injectable({
  providedIn: 'root'
})
export class AppConfigService {

  private appConfig: any;

  constructor(private http: HttpClient) { }

  loadAppConfig() {
    return this.http.get('/assets/config.json')
      .toPromise()
      .then(data => {
        this.appConfig = data;
      });
  }

  // This is an example property ... you can make it however you want.
  get apiBaseUrl() {

    if (!this.appConfig) {
      throw Error('Config file not loaded!');
    }

    return this.appConfig.apiBaseUrl;
  }
}

3. Загрузите конфигурацию, используя APP_INITIALISER

Чтобы обеспечить безопасную инъекцию AppConfigService при полной загрузке конфигурации, нам нужно загрузить конфигурацию во время запуска приложения. Важно отметить, что функция фабрики инициализации должна возвращать Promise чтобы Angular знал, что нужно дождаться завершения разрешения, прежде чем завершить запуск:

NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [
    {
      provide: APP_INITIALIZER,
      multi: true,
      deps: [AppConfigService],
      useFactory: (appConfigService: AppConfigService) => {
        return () => {
          //Make sure to return a promise!
          return appConfigService.loadAppConfig();
        };
      }
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Теперь вы можете вставить его куда угодно, и все настройки будут готовы к прочтению:

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {

  apiBaseUrl: string;

  constructor(private appConfigService: AppConfigService) {}

  ngOnInit(): void {
    this.apiBaseUrl = this.appConfigService.apiBaseUrl;
  }

}

Я не могу сказать это достаточно сильно, конфигурирование ваших URL-адресов API, так как конфигурация во время компиляции является анти-паттерном. Используйте конфигурацию во время выполнения.

Ответ 5

Вот мои два решения для этого

1. Хранить в файлах json

Просто создайте файл json и $http.get() в свой компонент методом $http.get(). Если бы мне было нужно это очень низко, то это хорошо и быстро.

2. Хранить с использованием услуг передачи данных

Если вы хотите хранить и использовать во всех компонентах или иметь большое использование, то лучше использовать службу данных. Как это:

  1. Просто создайте статическую папку внутри папки src/app.

  2. Создайте файл с именем fuels.ts в статической папке. Вы также можете хранить другие статические файлы здесь. Давайте определим ваши данные, как это. Предполагая, что у вас есть данные о топливе.

__

export const Fuels {

   Fuel: [
    { "id": 1, "type": "A" },
    { "id": 2, "type": "B" },
    { "id": 3, "type": "C" },
    { "id": 4, "type": "D" },
   ];
   }
  1. Создайте имя файла static.services.ts

__

import { Injectable } from "@angular/core";
import { Fuels } from "./static/fuels";

@Injectable()
export class StaticService {

  constructor() { }

  getFuelData(): Fuels[] {
    return Fuels;
  }
 }'
  1. Теперь вы можете сделать это доступным для каждого модуля

просто импортируйте файл app.module.ts вот так и меняйте провайдеров

import { StaticService } from './static.services';

providers: [StaticService]

Теперь используйте это как StaticService в любом модуле.

Все это.