Uygulama ayarları - Açısal yol mu?


90

App SettingsUygulamama bazı sabitler ve önceden tanımlanmış değerler içerecek bir bölüm eklemek istiyorum .

Okuduğum bu cevabı kullanır OpaqueTokenAma açısal önerilmiyor. Bu makale farklılıkları açıklıyor ancak tam bir örnek vermedi ve girişimlerim başarısız oldu.

İşte denediğim şey (bunun doğru yol olup olmadığını bilmiyorum):

//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');

Ve bu sabitleri kullanmak istediğim bileşen bu:

//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 {


}

Ama işe yaramıyor ve hata alıyorum.

Soru:

"App.settings" değerlerini Angular şekilde nasıl kullanabilirim?

plunker

NB Elbette, Enjekte edilebilir servis oluşturabilirim ve NgModule sağlayıcısına koyabilirim, Ama dediğim gibi, bunu InjectionTokenAngular yolla yapmak istiyorum .


Cevabımı güncel resmi belgelere göre buradan kontrol edebilirsiniz
JavierFuentes

@javier hayır. İki sağlayıcı aynı adı veriyorsa bağlantınızda bir sorun var, bu nedenle şimdi bir sorununuz var. Opaquetoken ile giriş
Royi Namir

[OpaqueToken kullanımdan kaldırıldı] biliyorsunuz. ( angular.io/api/core/OpaqueToken ) Bu Makale , Angular Providers'da isim çarpışmalarını nasıl önleyeceğinizi
anlatıyor

Biliyorum ama yine de bağlantılı makale yanlış.
Royi Namir

2
aşağıdaki bağlantı yeni açısal yapılandırma şeması mimarisini kullanmayı seven herkes için yararlı olabilir devblogs.microsoft.com/premier-developer/…
M_Farahmand

Yanıtlar:


58

Bunu InjectionTokens ile nasıl yapacağımı buldum (aşağıdaki örneğe bakın) ve projeniz kullanılarak oluşturulduysa, bir API uç noktası gibi statik için Angular CLIbulunan ortam dosyalarını kullanabilirsiniz , ancak projenizin gereksinimlerine bağlı olarak büyük olasılıkla sonuçlanacaksınız. ortam dosyaları yalnızca nesne değişmezleri olduğundan, 's kullanan enjekte edilebilir bir yapılandırma ortam değişkenlerini kullanabilir ve bir sınıf olduğu için uygulamadaki ilk http istek verileri, alt etki alanı gibi diğer faktörlere dayalı olarak yapılandırmak için mantık uygulanmış olabilir. , vb./environmentsapplication wide settingsInjectionToken

Enjeksiyon Belirteçleri Örneği

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

Şimdi onu herhangi bir bileşene, hizmete vb.

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

  // ...
}

Daha sonra, dışa aktarılan AppConfig'i kullanarak yapılandırmayı kontrol et yazabilirsiniz.


Hayır, ancak kelimenin tam anlamıyla ilk parçayı bir dosyaya kopyalayıp yapıştırabilir, app.module.ts dosyanıza ve DI onu herhangi bir yere aktarabilir ve konsola verebilirsiniz. Bunu bir tesisatçıda kurmak daha uzun sürerdi, sonra o adımları atmak olurdu.
mtpultz

Oh, bunun için zaten bir plunker olduğunu düşündüm :-) Teşekkür ederim.
Royi Namir


1
AppConfig arayüzünü / sınıfını dışa aktarmanız gerektiğine inanmıyorum. DI yaparken kesinlikle kullanmanıza gerek yoktur. Bunun tek bir dosyada çalışmasını sağlamak için arayüz yerine sınıf olması gerekiyordu, ancak bu önemli değil. Aslında stil kılavuzu, daha az kod anlamına geldiğinden ve bunları kullanarak check yazabileceğiniz için arabirimler yerine sınıfların kullanılmasını önerir. InjectionToken tarafından jenerikler aracılığıyla kullanımına gelince, bu dahil etmek isteyeceğiniz bir şey.
mtpultz

1
Azure'un ortam değişkenlerini ve JSON dönüştürme özelliklerini kullanarak API uç noktasını dinamik olarak enjekte etmeye çalışıyorum, ancak bu yanıt ortam dosyasından apiEndpoint'i alıyor gibi görünüyor. Yapılandırmadan nasıl alıp dışa aktarırsınız?
Archibald

138

Eğer kullanıyorsanız başka bir seçenek daha var:

Angular CLI, ortam dosyalarını sağlar src/environments(varsayılan olanlar environment.ts(dev) ve environment.prod.ts(üretim)).

Yapılandırma parametrelerini tüm environment.*dosyalarda sağlamanız gerektiğini unutmayın , örn.

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__'
};

ve bunları hizmetinizde kullanın (doğru ortam dosyası otomatik olarak seçilir):

api.service.ts

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

@Injectable()
export class ApiService {     

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

// ...
}

Github'daki (Angular CLI sürüm 6) veya resmi Angular kılavuzdaki (sürüm 7) uygulama ortamları hakkında daha fazla bilgi edinin .


3
onun bundle.I içinde yapılandırılmasını değiştirmek gerektiği gibi o da değiştirilir yapı taşırken iyi oldu.Ama çalışma benim hizmet etmeyen kodunda üretim taşındıktan sonra
kamalav

44
Bu, normal yazılım geliştirmede bir tür anti-modeldir; API url'si yalnızca yapılandırmadır. Bir uygulamayı farklı bir ortam için yeniden yapılandırmak yeniden derleme gerektirmemelidir. Bir kez oluşturulmalı, birçok kez kullanılmalıdır (üretim öncesi, hazırlık, üretim vb.).
Matt Tester

3
@MattTester Bu aslında artık resmi bir Angular-CLI hikayesi. Bu soruya daha iyi bir cevabınız varsa: göndermekten çekinmeyin!
tilo

7
ng oluşturulduktan sonra yapılandırılabilir mi?
NK

1
Oh tamam, yorumları yanlış anladım. Bunun bir anti-modele katkıda bulunduğuna katılıyorum, dinamik çalışma zamanı yapılandırmaları için bir hikaye olduğunu düşündüm.
Jens Bodal

89

environment.*.tsAPI URL yapılandırmanız için dosyaları kullanmanız tavsiye edilmez . Yapmanız gerektiği gibi görünüyor çünkü bu "çevre" kelimesinden bahsediyor.

Bunu kullanmak aslında derleme zamanı yapılandırmasıdır . API URL'sini değiştirmek isterseniz, yeniden oluşturmanız gerekecektir. Bu, yapmak zorunda olmadığın bir şey ... sadece arkadaş canlısı QA departmanınıza sorun :)

İhtiyacınız olan şey çalışma zamanı yapılandırmasıdır , yani uygulama başladığında yapılandırmasını yükler.

Diğer bazı cevaplar buna değiniyor, ancak fark, yapılandırmanın uygulama başlar başlamaz yüklenmesi gerektiğidir , böylece ihtiyaç duyulduğunda normal bir hizmet tarafından kullanılabilir.

Çalışma zamanı yapılandırmasını uygulamak için:

  1. /src/assets/Klasöre bir JSON yapılandırma dosyası ekleyin (böylece derlemeye kopyalanır)
  2. AppConfigServiceYapılandırmayı yüklemek ve dağıtmak için bir oluşturun
  3. Yapılandırmayı bir APP_INITIALIZER

1. Config dosyasını şuraya ekleyin: /src/assets

Başka bir klasöre ekleyebilirsiniz, ancak CLI'ye bunun angular.json. Varlıklar klasörünü kullanarak başlayın:

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

2. Oluşturun AppConfigService

Bu, config değerine ihtiyacınız olduğunda enjekte edilecek olan hizmettir:

@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. Yapılandırmayı bir APP_INITIALIZER

AppConfigServiceYapılandırmanın tam olarak yüklenmesiyle güvenli bir şekilde enjekte edilmesine izin vermek için , yapılandırmayı uygulama başlangıcında yüklememiz gerekir. Önemli olarak, başlatma fabrikası işlevinin a döndürmesi gerekir, Promiseböylece Angular, başlatmayı bitirmeden önce çözmeyi bitirene kadar beklemesi gerektiğini bilir:

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

Artık ihtiyacınız olan her yere enjekte edebilirsiniz ve tüm yapılandırma okumaya hazır olacaktır:

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

}

Yeterince güçlü bir şekilde söyleyemem, API URL'lerinizi derleme zamanı yapılandırması olarak yapılandırmak bir anti-modeldir . Çalışma zamanı yapılandırmasını kullanın.


4
Yerel dosya veya farklı bir hizmet, derleme zamanı yapılandırması bir API url'si için kullanılmamalıdır. Uygulamanızın bir ürün olarak satıldığını (satın alan kişinin yüklemesi için), onları derlemesini istemediğinizi, vb. Düşünün. Her iki durumda da, 2 yıl önce oluşturulmuş bir şeyi yeniden derlemek istemezsiniz API url'si değişti. Risk!!
Matt Tester

1
@Bloodhound Birden fazlasına sahip olabilirsiniz APP_INITIALIZERama onları kolayca birbirine bağımlı hale getirebileceğinizi sanmıyorum. Soracak güzel bir sorunuz var gibi görünüyor, bu yüzden buraya bağlantı verebilirsiniz.
Matt Tester

2
@MattTester - Angular bu özelliği kullanırsa sorunumuzu çözecektir: github.com/angular/angular/issues/23279#issuecomment-528417026
Mike Becatti

2
@CrhistianRamirez Uygulamanın bakış açısından: yapılandırma, çalışma zamanına kadar bilinmez ve statik dosya derlemenin dışındadır ve dağıtım sırasında birçok şekilde ayarlanabilir. Statik dosya, hassas olmayan yapılandırma için uygundur. Aynı teknikle bir API veya başka bir korumalı uç nokta mümkündür, ancak onu korumak için nasıl kimlik doğrulaması yapılacağı bir sonraki zorluğunuzdur.
Matt Tester

1
@DaleK Satır aralarını okurken, Web Dağıtımı kullanarak dağıtıyorsunuz. Azure DevOps gibi bir dağıtım işlem hattı kullanıyorsanız, sonraki adım olarak yapılandırma dosyasını doğru şekilde ayarlamak mümkündür. Yapılandırmanın ayarı, varsayılan yapılandırma dosyasındaki değerleri geçersiz kılan dağıtım işleminin / kanalının sorumluluğundadır. Umarım netleştirir.
Matt Tester

8

İşte benim çözümüm, yeniden oluşturulmadan değişikliklere izin vermek için .json'dan yükleniyor

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

ve config.json

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

1
Bu yaklaşımdaki sorun, config.json'un dünyaya açık olmasıdır. Birinin www.mywebsite.com/assetts/config.json yazmasını nasıl engellersiniz?
Alberto L. Bonfiglio

1
@ AlbertoL.Bonfiglio sunucuyu dışarıdan config.json dosyasına erişime izin vermeyecek (veya genel erişimi olmayan bir dizine yerleştirecek şekilde) yapılandırırsınız
Alex Pandrea

Bu benim de en sevdiğim çözüm, ancak yine de güvenlik riskleri konusunda endişeli.
ViqMontana

7
Lütfen, doğru anlamama yardım edebilir misin? Açısal ortamlar için gelenekselden daha riskli nasıldır? Tüm içeriğinin environments.prod.tssonrasÖ ng build --prodbazı olacak .jsbir noktada dosyası. Gizlenmiş olsa bile, gelen veriler environments.prod.tsaçık metin halinde olacaktır. Ve tüm .js dosyaları gibi, son kullanıcı makinesinde de mevcut olacaktır.
igann

5
@ AlbertoL.Bonfiglio Bir Angular uygulaması, doğası gereği bir istemci uygulaması olduğundan ve JavaScript, verilerin ve yapılandırmanın etrafından geçirilmesi için kullanılacağından, içinde kullanılan hiçbir gizli yapılandırma olmamalıdır; tüm gizli yapılandırma tanımları, bir kullanıcının tarayıcısının veya tarayıcı araçlarının erişemediği bir API katmanının arkasında olmalıdır. Bir API'nin temel URI'si gibi değerler, herkesin erişmesi için uygundur, çünkü API'nin, kullanıcının oturum açmasına bağlı olarak kendi kimlik bilgilerine ve güvenliğine (https üzerinden taşıyıcı belirteci) sahip olması gerekir.
Tommy Elliott

5

Zavallı adamın yapılandırma dosyası:

Body etiketinde ilk satır olarak index.html dosyanıza ekleyin:

<script lang="javascript" src="assets/config.js"></script>

Varlıklar / config.js ekleyin:

var config = {
    apiBaseUrl: "http://localhost:8080"
}

Config.ts ekleyin:

export const config: AppConfig = window['config']

export interface AppConfig {
    apiBaseUrl: string
}

Cidden, +1, bir çözümü en temel bileşenlerine indirgemek ve tür tutarlılığını sürdürmek için.
Aydınlık

5

Bunun APP_INITIALIZERiçin bir kullanmanın, diğer servis sağlayıcıların yapılandırmanın enjekte edilmesini gerektirdiği durumlarda işe yaramadığını buldum . APP_INITIALIZERÇalıştırılmadan önce somutlaştırılabilirler .

fetchBir config.json dosyasını okumak için kullanan platformBrowserDynamic()ve kök modülün önyüklenmesinden önce bir parametrede bir enjeksiyon belirteci kullanarak bunu sağlayan başka çözümler gördüm . Ancak fetchtüm tarayıcılarda ve özellikle hedeflediğim mobil cihazlar için Web Görünümü tarayıcılarında desteklenmez.

Aşağıdaki, hem PWA hem de mobil cihazlar (WebView) için benim için çalışan bir çözümdür. Not: Şimdiye kadar yalnızca Android'de test ettim; evden çalışmak, oluşturmak için bir Mac'e erişimim olmadığı anlamına geliyor.

İçinde main.ts:

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { APP_CONFIG } from './app/lib/angular/injection-tokens';

function configListener() {
  try {
    const configuration = JSON.parse(this.responseText);

    // pass config to bootstrap process using an injection token
    platformBrowserDynamic([
      { provide: APP_CONFIG, useValue: configuration }
    ])
      .bootstrapModule(AppModule)
      .catch(err => console.error(err));

  } catch (error) {
    console.error(error);
  }
}

function configFailed(evt) {
  console.error('Error: retrieving config.json');
}

if (environment.production) {
  enableProdMode();
}

const request = new XMLHttpRequest();
request.addEventListener('load', configListener);
request.addEventListener('error', configFailed);
request.open('GET', './assets/config/config.json');
request.send();

Bu kod:

  1. config.jsondosya için eşzamansız bir istek başlatır .
  2. İstek tamamlandığında, JSON'yi bir Javascript nesnesine ayrıştırır
  3. değeri APP_CONFIG, önyüklemeden önce enjeksiyon belirtecini kullanarak sağlar .
  4. Ve son olarak kök modülü önyükler.

APP_CONFIGdaha sonra içindeki herhangi bir ek sağlayıcıya enjekte edilebilir app-module.tsve tanımlanacaktır. Örneğin, FIREBASE_OPTIONSenjeksiyon jetonunu @angular/fireaşağıdakilerden başlatabilirim :

{
      provide: FIREBASE_OPTIONS,
      useFactory: (config: IConfig) => config.firebaseConfig,
      deps: [APP_CONFIG]
}

Tüm bunları, çok yaygın bir gereksinim için şaşırtıcı derecede zor (ve hilekâr) bir şey buluyorum. Umarım yakın gelecekte, asenkron sağlayıcı fabrikaları için destek gibi daha iyi bir yol olacaktır.

Eksiksizlik için kodun geri kalanı ...

İçinde app/lib/angular/injection-tokens.ts:

import { InjectionToken } from '@angular/core';
import { IConfig } from '../config/config';

export const APP_CONFIG = new InjectionToken<IConfig>('app-config');

ve içinde app/lib/config/config.tsJSON yapılandırma dosyam için arabirimi tanımlıyorum:

export interface IConfig {
    name: string;
    version: string;
    instance: string;
    firebaseConfig: {
        apiKey: string;
        // etc
    }
}

Yapılandırma şurada saklanır assets/config/config.json:

{
  "name": "my-app",
  "version": "#{Build.BuildNumber}#",
  "instance": "localdev",
  "firebaseConfig": {
    "apiKey": "abcd"
    ...
  }
}

Not: Build.BuildNumber eklemek ve dağıtılırken farklı dağıtım ortamları için diğer ayarları değiştirmek için bir Azure DevOps görevi kullanıyorum.


2

İşte bunun için iki çözümüm

1. json dosyalarında saklayın

Sadece bir json dosyası oluşturun ve bileşeninize $http.get()yöntemle girin. Buna çok ihtiyacım olsaydı, o zaman iyi ve hızlıdır.

2. Veri hizmetlerini kullanarak depolayın

Tüm bileşenlerde depolamak ve kullanmak istiyorsanız veya geniş kullanıma sahipseniz, veri hizmetini kullanmak daha iyidir. Bunun gibi :

  1. Sadece klasörün içinde statik klasör oluşturun src/app.

  2. fuels.tsStatik klasör olarak adlandırılmış bir dosya oluşturun . Diğer statik dosyaları da burada saklayabilirsiniz. Verilerinizi bu şekilde tanımlayalım. Yakıt verisine sahip olduğunuzu varsayarsak.

__

export const Fuels {

   Fuel: [
    { "id": 1, "type": "A" },
    { "id": 2, "type": "B" },
    { "id": 3, "type": "C" },
    { "id": 4, "type": "D" },
   ];
   }
  1. Static.services.ts bir dosya adı oluşturun

__

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

@Injectable()
export class StaticService {

  constructor() { }

  getFuelData(): Fuels[] {
    return Fuels;
  }
 }`
  1. Artık bunu her modül için kullanılabilir hale getirebilirsiniz

sadece app.module.ts dosyasına bu şekilde içe aktarın ve sağlayıcılarda değiştirin

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

providers: [StaticService]

Şimdi bunu StaticServiceherhangi bir modülde olduğu gibi kullanın .

Bu kadar.


Yeniden derlemeniz gerekmediği için iyi bir çözüm. Çevre, onu kodun içine gömmek gibidir. İğrenç. +1
Terrance00


0

Bu sorunu yıllar önce ben katılmadan önce yaşadık ve kullanıcı ve çevre bilgileri için yerel depolama kullanan bir çözüm bulduk. Tam olarak açısal 1.0 gün. Eskiden çalışma zamanında dinamik olarak bir js dosyası oluşturuyorduk ve bu daha sonra oluşturulan api url'lerini global bir değişkene yerleştirirdi. Bugünlerde biraz daha fazla OOP kullanıyoruz ve yerel depolamayı hiçbir şey için kullanmayın.

Hem ortamı belirlemek hem de api url oluşturmak için daha iyi bir çözüm yarattım.

Bu nasıl farklılık gösterir?

Config.json dosyası yüklenmedikçe uygulama yüklenmeyecektir. Daha yüksek bir SOC derecesi oluşturmak için fabrika işlevlerini kullanır. Bunu bir hizmet olarak özetleyebilirim, ancak dosyanın farklı bölümleri arasındaki tek benzerlik, dosyada birlikte var olmaları olduğunda hiçbir neden görmedim. Bir fabrika işlevine sahip olmak, bir işlevi kabul edebiliyorsa işlevi doğrudan bir modüle geçirmemi sağlar. Son olarak, fabrika fonksiyonları kullanıma hazır olduğunda InjectionTokens'ı kurmak için daha kolay bir zamanım var.

Dezavantajlar?

Yapılandırmak istediğiniz modül bir fabrika işlevinin forRoot () veya forChild () 'e geçirilmesine izin vermiyorsa ve başka bir yol yoksa, bu kurulumu (ve diğer yanıtların çoğunu) kullanırken şansınız kalmaz. bir fabrika işlevini kullanarak paketi yapılandırın.

Talimatlar

  1. Bir json dosyasını almak için getirme özelliğini kullanarak nesneyi pencerede depoluyorum ve özel bir olay oluşturuyorum. - whatwg-fetch'i kurmayı ve IE uyumluluğu için polyfills.ts dosyanıza eklemeyi unutmayın
  2. Özel etkinliği dinleyen bir olay dinleyicisine sahip olun.
  3. Olay dinleyicisi olayı alır, bir gözlemlenebilir nesneye geçmek için pencereden nesneyi alır ve pencerede neyin depolandığını siler.
  4. Bootstrap Açısal

- Çözümümün gerçekten farklı olmaya başladığı yer burası -

  1. Yapısı config.json'unuzu temsil eden bir arabirimi dışa aktaran bir dosya oluşturun - tür tutarlılığına gerçekten yardımcı olur ve sonraki kod bölümü bir tür gerektirir ve belirtmeyin {}veya anydaha somut bir şey belirtebileceğinizi bildiğiniz zaman
  2. Ayrıştırılmış json dosyasını 3. adımda ileteceğiniz BehaviorSubject'i oluşturun.
  3. SOC'yi korumak için yapılandırmanızın farklı bölümlerine referans vermek için fabrika işlevlerini kullanın
  4. Fabrika işlevlerinizin sonucuna ihtiyaç duyan sağlayıcılar için InjectionTokens oluşturun

- ve / veya -

  1. Fabrika işlevlerini doğrudan forRoot () veya forChild () yöntemlerinde bir işlevi kabul edebilen modüllere aktarın.

- main.ts

Bir olay dinleyicisi oluşturmadan önce, ana.ts içindeki kod çalıştırılmadan önce pencerenin ["ortam"] başka yollarla doldurulduğu bir çözüm olasılığına izin vermek için pencerenin ["ortam"] doldurulmadığını kontrol ediyorum.

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { configurationSubject } from './app/utils/environment-resolver';

var configurationLoadedEvent = document.createEvent('Event');
configurationLoadedEvent.initEvent('config-set', true, true);
fetch("../../assets/config.json")
.then(result => { return result.json(); })
.then(data => {
  window["environment"] = data;
  document.dispatchEvent(configurationLoadedEvent);
}, error => window.location.reload());

/*
  angular-cli only loads the first thing it finds it needs a dependency under /app in main.ts when under local scope. 
  Make AppModule the first dependency it needs and the rest are done for ya. Event listeners are 
  ran at a higher level of scope bypassing the behavior of not loading AppModule when the 
  configurationSubject is referenced before calling platformBrowserDynamic().bootstrapModule(AppModule)

  example: this will not work because configurationSubject is the first dependency the compiler realizes that lives under 
  app and will ONLY load that dependency, making AppModule an empty object.

  if(window["environment"])
  {
    if (window["environment"].production) {
      enableProdMode();
    }
    configurationSubject.next(window["environment"]);
    platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
  }
*/
if(!window["environment"]) {
  document.addEventListener('config-set', function(e){
    if (window["environment"].production) {
      enableProdMode();
    }
    configurationSubject.next(window["environment"]);
    window["environment"] = undefined;
    platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
  });
}

--- çevre çözücüler.ts

Artıklık için ["ortam"] penceresini kullanarak BehaviorSubject'e bir değer atıyorum. Yapılandırmanızın önceden yüklenmiş olduğu ve main.ts içindeki kod da dahil olmak üzere Angular uygulama kodunuzun herhangi biri çalıştırıldığında pencere ["ortam"] zaten doldurulmuş olan bir çözüm geliştirebilirsiniz.

import { BehaviorSubject } from "rxjs";
import { IConfig } from "../config.interface";

const config = <IConfig>Object.assign({}, window["environment"]);
export const configurationSubject = new BehaviorSubject<IConfig>(config);
export function resolveEnvironment() {
  const env = configurationSubject.getValue().environment;
  let resolvedEnvironment = "";
  switch (env) {
 // case statements for determining whether this is dev, test, stage, or prod
  }
  return resolvedEnvironment;
}

export function resolveNgxLoggerConfig() {
  return configurationSubject.getValue().logging;
}

- app.module.ts - Daha kolay anlaşılması için kaldırıldı

Eğlenceli gerçek! NGXLogger'ın eski sürümleri, bir nesneyi LoggerModule.forRoot () 'a iletmenizi gerektiriyordu. Aslında, LoggerModule hala var! NGXLogger, kurulum için fabrika işlevini kullanmanıza izin veren, geçersiz kılabileceğiniz LoggerConfig'i nazikçe ortaya çıkarır.

import { resolveEnvironment, resolveNgxLoggerConfig, resolveSomethingElse } from './environment-resolvers';
import { LoggerConfig } from 'ngx-logger';
@NgModule({
    modules: [
        SomeModule.forRoot(resolveSomethingElse)
    ],
    providers:[
        {
            provide: ENVIRONMENT,
            useFactory: resolveEnvironment
        },
        { 
            provide: LoggerConfig,
            useFactory: resolveNgxLoggerConfig
        }
    ]
})
export class AppModule

Ek

API URL'lerimin oluşturulmasını nasıl çözdüm?

Her url'nin bir yorum aracılığıyla ne yaptığını anlayabilmek ve TypeScript'in javascript'e (IMO) kıyasla en büyük gücü bu olduğundan, yazım denetimi yapmak istedim. Ayrıca, diğer geliştiricilerin yeni uç noktalar ve olabildiğince sorunsuz api'ler ekleyebileceği bir deneyim oluşturmak istedim.

Ortamı (dev, test, stage, prod, "" vb.) Alan bir sınıf oluşturdum ve bu değeri, görevi her API koleksiyonu için temel url oluşturmak olan bir dizi sınıfa [1-N] geçirdim . Her ApiCollection, her API koleksiyonu için temel URL'yi oluşturmaktan sorumludur. Kendi API'lerimiz, bir satıcının API'ları veya hatta bir harici bağlantı olabilir. Bu sınıf, oluşturulan temel url'yi içerdiği sonraki her api'ye iletir. Çıplak kemik örneği görmek için aşağıdaki kodu okuyun. Bir kez kurulduktan sonra, başka bir geliştiricinin bir Api sınıfına başka hiçbir şeye dokunmadan başka bir uç nokta eklemesi çok basittir.

TLDR; bellek optimizasyonu için temel OOP ilkeleri ve tembel alıcılar

@Injectable({
    providedIn: 'root'
})
export class ApiConfig {
    public apis: Apis;

    constructor(@Inject(ENVIRONMENT) private environment: string) {
        this.apis = new Apis(environment);
    }
}

export class Apis {
    readonly microservices: MicroserviceApiCollection;

    constructor(environment: string) {
        this.microservices = new MicroserviceApiCollection(environment);
    }
}

export abstract class ApiCollection {
  protected domain: any;

  constructor(environment: string) {
      const domain = this.resolveDomain(environment);
      Object.defineProperty(ApiCollection.prototype, 'domain', {
          get() {
              Object.defineProperty(this, 'domain', { value: domain });
              return this.domain;
          },
          configurable: true
      });
  }
}

export class MicroserviceApiCollection extends ApiCollection {
  public member: MemberApi;

  constructor(environment) {
      super(environment);
      this.member = new MemberApi(this.domain);
  }

  resolveDomain(environment: string): string {
      return `https://subdomain${environment}.actualdomain.com/`;
  }
}

export class Api {
  readonly base: any;

  constructor(baseUrl: string) {
      Object.defineProperty(this, 'base', {
          get() {
              Object.defineProperty(this, 'base',
              { value: baseUrl, configurable: true});
              return this.base;
          },
          enumerable: false,
          configurable: true
      });
  }

  attachProperty(name: string, value: any, enumerable?: boolean) {
      Object.defineProperty(this, name,
      { value, writable: false, configurable: true, enumerable: enumerable || true });
  }
}

export class MemberApi extends Api {

  /**
  * This comment will show up when referencing this.apiConfig.apis.microservices.member.memberInfo
  */
  get MemberInfo() {
    this.attachProperty("MemberInfo", `${this.base}basic-info`);
    return this.MemberInfo;
  }

  constructor(baseUrl: string) {
    super(baseUrl + "member/api/");
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.