App.settings - कोणीय तरीका?


87

मैं App Settingsअपने App में एक सेक्शन जोड़ना चाहता हूं जहां इसमें कुछ const और पूर्व निर्धारित मान होंगे।

मैंने पहले से ही इस उत्तर को पढ़ा है जो इसका उपयोग करता है OpaqueTokenलेकिन यह कोणीय में पदावनत है। यह लेख अंतर बताता है, लेकिन यह एक पूर्ण उदाहरण प्रदान नहीं करता था, और मेरे प्रयास असफल रहे थे।

यहाँ वही है जो मैंने कोशिश की है (मुझे नहीं पता कि यह सही तरीका है):

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

एनबी ज़रूर मैं इंजेक्टेबल सर्विस बना सकता हूं और उसे एनजीमोड्यूल के प्रदाता में डाल सकता हूं, लेकिन जैसा कि मैंने कहा कि मैं इसे एंगुलर तरीके से करना चाहता हूं InjectionToken


तुम मेरे जवाब की जांच कर सकते यहाँ वर्तमान आधिकारिक दस्तावेज में आधारित
JavierFuentes

@ जेवियर नं। आपके लिंक में एक समस्या है यदि दो प्रदाता एक ही नाम की आपूर्ति करते हैं तो आपको अब एक समस्या है। Opaquetoken Entring
Royi Namir

तुम्हें पता है [OpaqueToken पदावनत है]। ( angular.io/api/core/OpaqueToken ) यह आलेख बात करता है कि कोणीय प्रदाताओं में नाम टकराव को कैसे रोका
जाए

हां मैं iknow, लेकिन अभी भी जुड़ा हुआ लेख गलत है।
रॉय नमिर

2
नीचे दिया गया लिंक हर उस व्यक्ति के लिए मददगार हो सकता है जो कोणीय विन्यास के नए आर्किटेक्चर का उपयोग करना पसंद करता है स्कीमा devblogs.microsoft.com/premier-developer/…
M_Farahmand

जवाबों:


56

मुझे यह पता चला कि यह कैसे करें इंजेक्शनकोन्स के साथ (नीचे उदाहरण देखें), और यदि आपकी परियोजना का उपयोग करके बनाया गया था, Angular CLIतो आप एक एपीआई एंडपॉइंट की तरह /environmentsस्थिर में पाए जाने वाले पर्यावरण फ़ाइलों का उपयोग कर सकते हैं application wide settings, लेकिन आपकी परियोजना की आवश्यकताओं के आधार पर आप सबसे अधिक संभावना समाप्त करेंगे पर्यावरण फ़ाइलों का उपयोग करने के बाद से दोनों केवल वस्तु शाब्दिक हैं, जबकि एक इंजेक्टेबल कॉन्फ़िगरेशन 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 का उपयोग करके कॉन्फ़िगर की जांच कर सकते हैं।


नहीं, लेकिन आप सचमुच एक फ़ाइल में पहले भाग को कॉपी और पेस्ट कर सकते हैं, इसे अपने app.module.ts फ़ाइल में आयात कर सकते हैं, और इसे कहीं भी डि कर सकते हैं और इसे कंसोल में आउटपुट कर सकते हैं। मुझे इसे एक प्लंकर में स्थापित करने में अधिक समय लगेगा तब यह उन चरणों को करना होगा।
mtpultz

ओह, मैंने सोचा था कि आपके पास पहले से ही इस के लिए एक प्लंकर है :) धन्यवाद।
रॉय नमिर

उन लोगों के लिए जो चाहते हैं: plnkr.co/edit/2YMZk5mhP1B4jTgA37B8?p=preview
Royi Namir

1
मुझे विश्वास नहीं है कि आपको AppConfig इंटरफ़ेस / क्लास निर्यात करने की आवश्यकता है। आपको निश्चित रूप से DI करते समय इसका उपयोग करने की आवश्यकता नहीं है। एक फ़ाइल में यह काम करने के लिए एक इंटरफ़ेस के बजाय एक वर्ग होना चाहिए था, लेकिन यह कोई फर्क नहीं पड़ता। वास्तव में स्टाइल गाइड सुझाव के बजाय कक्षाओं का उपयोग करने का सुझाव देता है क्योंकि इसका मतलब कम कोड है और आप अभी भी उनका उपयोग करके चेक टाइप कर सकते हैं। इंजेक्शन के माध्यम से इसके उपयोग के संबंध में जेनरिक के माध्यम से जो कुछ आप शामिल करना चाहते हैं।
mtpultz

1
मैं एज़्योर के पर्यावरण चर और JSON ट्रांसफ़ॉर्म सुविधाओं का उपयोग करके गतिशील रूप से एपीआई एंडपॉइंट को इंजेक्ट करने की कोशिश कर रहा हूं, लेकिन ऐसा लगता है कि यह उत्तर सिर्फ पर्यावरण फ़ाइल से एपिंडपॉइंट को मिलता है। आप इसे कैसे कॉन्फ़िगर करेंगे और इसे निर्यात करेंगे?
आर्चीबाल्ड

138

यदि आप उपयोग कर रहे हैं , अभी तक एक और विकल्प है:

कोणीय सीएलआई पर्यावरण की फाइलें प्रदान करता है src/environments(डिफ़ॉल्ट वाले हैं environment.ts(देव) और environment.prod.ts(उत्पादन))।

ध्यान दें कि आपको सभी environment.*फ़ाइल में कॉन्फिग पैरामीटर प्रदान करने की आवश्यकता है , उदाहरण के लिए,

पर्यावरण :

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

पर्यावरण

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) पर या आधिकारिक कोणीय गाइड (संस्करण 7) पर अनुप्रयोग वातावरण पर अधिक पढ़ें ।


2
इसका काम ठीक है। लेकिन निर्माण करते समय इसे बंडल के रूप में भी बदल दिया जाता है। मुझे उत्पादन में जाने के बाद कोड में नहीं मेरी सेवा में विन्यास को बदलना चाहिए
kamalav

42
यह सामान्य सॉफ़्टवेयर विकास में कुछ हद तक एक विरोधी पैटर्न है; एपीआई यूआरएल सिर्फ विन्यास है। एक अलग वातावरण के लिए किसी ऐप को फिर से कॉन्फ़िगर करने के लिए इसे फिर से नहीं बनाना चाहिए। इसे एक बार बनाया जाना चाहिए, कई बार तैनात करना (पूर्व-ठेस, मंचन, ठेस, आदि)।
मैट परीक्षक

3
@MattTester यह वास्तव में अब एक आधिकारिक कोणीय-सीएलआई कहानी है। यदि आपके पास इस प्रश्न का बेहतर उत्तर है: इसे पोस्ट करने के लिए स्वतंत्र महसूस करें!
तिलो

7
क्या यह एनजी बिल्ड के बाद कॉन्फ़िगर करने योग्य है?
NK

1
ओह ठीक है, मैंने टिप्पणियों को गलत बताया। मैं सहमत हूँ कि यह एक विरोधी पैटर्न को उधार देता है, मुझे लगता है कि गतिशील रन टाइम कॉन्फ़िगरेशन के लिए एक कहानी थी।
जेन्स बोडल

83

environment.*.tsआपके API URL कॉन्फ़िगरेशन के लिए फ़ाइलों का उपयोग करना उचित नहीं है। ऐसा लगता है कि आपको ऐसा करना चाहिए क्योंकि यह "पर्यावरण" शब्द का उल्लेख करता है।

इसका उपयोग करना वास्तव में संकलन-समय कॉन्फ़िगरेशन है । यदि आप एपीआई यूआरएल बदलना चाहते हैं, तो आपको फिर से निर्माण करना होगा। यह कुछ ऐसा है जो आप नहीं करना चाहते हैं ... बस अपने दोस्ताना क्यूए विभाग से पूछें :)

आपको रनटाइम कॉन्फ़िगरेशन की आवश्यकता होती है , अर्थात जब यह शुरू होता है तो ऐप इसका कॉन्फ़िगरेशन लोड कर देता है।

इस पर कुछ अन्य उत्तर स्पर्श करते हैं, लेकिन अंतर यह है कि ऐप के शुरू होते ही कॉन्फ़िगरेशन को लोड करना पड़ता है , ताकि जब भी जरूरत हो एक सामान्य सेवा द्वारा इसका उपयोग किया जा सके।

रनटाइम कॉन्फ़िगरेशन लागू करने के लिए:

  1. /src/assets/फ़ोल्डर में JSON कॉन्फ़िग फ़ाइल जोड़ें (ताकि बिल्ड पर प्रतिलिपि बनाई गई हो)
  2. AppConfigServiceकॉन्फ़िगरेशन को लोड करने और वितरित करने के लिए एक बनाएं
  3. एक का उपयोग कर विन्यास लोड करें APP_INITIALIZER

1. करने के लिए कॉन्फ़िगर फ़ाइल जोड़ें /src/assets

आप इसे किसी अन्य फ़ोल्डर में जोड़ सकते हैं, लेकिन आपको सीएलआई को बताना होगा कि यह एक परिसंपत्ति है 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_INITIALIZER

AppConfigServiceसुरक्षित रूप से इंजेक्ट होने की अनुमति देने के लिए, पूरी तरह से लोड किए गए कॉन्फ़िगरेशन के साथ, हमें एप्लिकेशन स्टार्टअप समय पर कॉन्फ़िगरेशन को लोड करना होगा। महत्वपूर्ण रूप से, प्रारंभिक कारखाने के कार्य को वापस करने की आवश्यकता है Promiseताकि स्टार्टअप को खत्म करने से पहले कोणीय पूरा होने तक इंतजार करना जानता हो।

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

}

मैं इसे दृढ़ता से पर्याप्त नहीं कह सकता हूं, आपके एपीआई यूआरएल को कॉन्फ़िगर करना क्योंकि संकलन-समय कॉन्फ़िगरेशन एक विरोधी पैटर्न है । रनटाइम कॉन्फ़िगरेशन का उपयोग करें।


4
स्थानीय फ़ाइल या अलग-अलग सेवा, एक एपीआई यूआरएल के लिए संकलन-समय के विन्यास का उपयोग नहीं किया जाना चाहिए। कल्पना कीजिए कि यदि आपका ऐप एक उत्पाद के रूप में बेचा जाता है (क्रेता स्थापित करने के लिए), तो आप नहीं चाहते हैं कि वे इसे संकलित करें, आदि। किसी भी तरह, आप उस चीज़ को फिर से संकलित नहीं करना चाहते हैं जो 2 साल पहले बनाया गया था, सिर्फ इसलिए एपीआई url बदल गया। जोखिम!!
मैट टेस्टर

1
@ फ्लडहाउंड आपके पास एक से अधिक हो सकते हैं APP_INITIALIZERलेकिन मुझे नहीं लगता कि आप उन्हें आसानी से एक दूसरे पर निर्भर बना सकते हैं। ऐसा लगता है कि आपके पास पूछने के लिए एक अच्छा सवाल है, तो शायद यहां लिंक करें?
मैट टेस्टर

2
@MattTester - अगर कोणीय कभी इस सुविधा को लागू करता है तो यह हमारी समस्या को हल करेगा: github.com/angular/angular/issues/23279#issuecomment-528417026
माइक बिकट्टी

2
@ChhistianRamirez यह एप्लिकेशन के दृष्टिकोण से है: कॉन्फ़िगरेशन रनटाइम तक ज्ञात नहीं है और स्थिर फ़ाइल बिल्ड के बाहर है और इसे कई तरीकों से सेट समय पर सेट किया जा सकता है। गैर-संवेदनशील कॉन्फ़िगरेशन के लिए स्टेटिक फ़ाइल ठीक है। एक एपीआई या कुछ अन्य संरक्षित एंड-पॉइंट एक ही तकनीक से संभव है, लेकिन इसे संरक्षित करने के लिए कैसे प्रमाणित किया जाए यह आपकी अगली चुनौती है।
मैट टेस्टर

1
@DaleK लाइनों के बीच पढ़ना, आप वेब परिनियोजन का उपयोग करके तैनात कर रहे हैं। यदि आप Azure DevOps की तरह एक परिनियोजन पाइपलाइन का उपयोग कर रहे हैं, तो अगले चरण के रूप में कॉन्फ़िगरेशन फ़ाइल को सही ढंग से सेट करना संभव है। कॉन्फ़िगरेशन की सेटिंग परिनियोजन प्रक्रिया / पाइपलाइन की जिम्मेदारी है, जो डिफ़ॉल्ट कॉन्फ़िग फ़ाइल में मानों को ओवरराइड कर सकती है। आशा है कि स्पष्ट करता है।
मैट परीक्षक

8

यहाँ मेरा समाधान है, बिना पुनर्निर्माण के बदलाव की अनुमति देने के लिए .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"
}

1
इस दृष्टिकोण के साथ समस्या यह है कि config.json दुनिया के लिए खुला है। आप किसी को www.mywebsite.com/assetts/config.json टाइप करने से कैसे रोकेंगे?
अल्बर्टो एल। बोनफिग्लियो

1
@ AlbertoL.Bonfiglio आप सर्वर को कॉन्फ़िगर करने के लिए बाहर से पहुंच की अनुमति नहीं देते हैं। config फाइल (या इसे एक निर्देशिका में रखें जिसमें कोई सार्वजनिक पहुंच नहीं है)
एलेक्स पंड्रिया

यह मेरा पसंदीदा समाधान भी है, लेकिन अभी भी सुरक्षा जोखिमों के बारे में चिंतित हूं।
वकास

7
कृपया, क्या आप इसे ठीक करने में मेरी मदद कर सकते हैं? यह कोणीय वातावरण के लिए पारंपरिक से अधिक जोखिम भरा कैसे है? के environments.prod.tsबाद की पूरी सामग्री कुछ बिंदु ng build --prodपर कुछ .jsफ़ाइल में होगी । यदि बाधित है, तो भी इसका डेटा environments.prod.tsस्पष्ट पाठ में होगा। और सभी .js फ़ाइलों के रूप में, यह एंड यूज़र मशीन पर उपलब्ध होगा।
igann

5
@ AlbertoL.Bonfiglio क्योंकि एक कोणीय एप्लिकेशन प्रकृति से एक ग्राहक अनुप्रयोग है, और जावास्क्रिप्ट का उपयोग डेटा और कॉन्फ़िगरेशन के आसपास से गुजरने के लिए किया जाएगा, इसमें कोई गुप्त कॉन्फ़िगरेशन नहीं होना चाहिए; सभी गुप्त कॉन्फ़िगरेशन परिभाषाएँ API परत के पीछे होनी चाहिए जहाँ उपयोगकर्ता का ब्राउज़र या ब्राउज़र उपकरण इसे एक्सेस नहीं कर सकता है। एपीआई के आधार यूआरआई जैसे मूल्य जनता के लिए उपयोग करने के लिए ठीक हैं क्योंकि एपीआई में उपयोगकर्ता लॉगिंग (https पर टोकन टोकन) के आधार पर अपनी साख और सुरक्षा होनी चाहिए।
टॉमी इलियट

4

गरीब आदमी की कॉन्फ़िगरेशन फ़ाइल:

बॉडी टैग में पहले lain के रूप में अपने index.html में जोड़ें:

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

संपत्ति / config.js जोड़ें:

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

Config.ts जोड़ें:

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

export interface AppConfig {
    apiBaseUrl: string
}

गंभीरता से, यह घटकों के सबसे बुनियादी और अभी भी प्रकार स्थिरता बनाए रखने में एक समाधान को उबालने के लिए +1 है।
चमकदार

4

मैंने पाया कि इसके लिए उपयोग APP_INITIALIZERकरना उन स्थितियों में काम नहीं करता है जहां अन्य सेवा प्रदाताओं को इंजेक्शन लगाने की आवश्यकता होती है। APP_INITIALIZERचलाने से पहले उन्हें तुरंत लगाया जा सकता है।

मैंने अन्य समाधान देखे हैं जो fetchएक config.json फ़ाइल को पढ़ने के लिए उपयोग करते हैं और platformBrowserDynamic()रूट मॉड्यूल को बूटस्ट्रैप करने से पहले एक पैरामीटर में एक इंजेक्शन टोकन का उपयोग करके प्रदान करते हैं । लेकिन fetchसभी ब्राउज़रों और विशेष रूप से वेबव्यू ब्राउज़रों में मेरे द्वारा लक्षित मोबाइल उपकरणों के लिए समर्थित नहीं है।

निम्नलिखित एक समाधान है जो मेरे लिए PWA और मोबाइल डिवाइस (WebView) दोनों के लिए काम करता है। नोट: मैंने अभी तक केवल Android में परीक्षण किया है; घर से काम करने का मतलब है मेरे पास बनाने के लिए मैक तक पहुंच नहीं है।

इन 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();

यह कोड:

  1. config.jsonफ़ाइल के लिए एक async अनुरोध बंद करता है ।
  2. जब अनुरोध पूरा हो जाता है, तो JSON को जावास्क्रिप्ट ऑब्जेक्ट में पार्स करता है
  3. APP_CONFIGबूटस्ट्रैपिंग से पहले इंजेक्शन टोकन का उपयोग करके मूल्य प्रदान करता है ।
  4. और अंत में मूल मॉड्यूल को बूटस्ट्रैप करता है।

APP_CONFIGफिर किसी भी अतिरिक्त प्रदाताओं में इंजेक्ट किया जा सकता है app-module.tsऔर इसे परिभाषित किया जाएगा। उदाहरण के लिए, मैं निम्नलिखित के साथ FIREBASE_OPTIONSइंजेक्शन टोकन को इनिशियलाइज़ कर सकता हूं @angular/fire:

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

मुझे यह पूरी बात एक बहुत ही सामान्य आवश्यकता के लिए आश्चर्यजनक रूप से कठिन (और हैकेजी) लगती है। उम्मीद है कि निकट भविष्य में एक बेहतर तरीका होगा, जैसे कि, async प्रदाता कारखानों के लिए समर्थन।

पूर्णता के लिए शेष कोड ...

इन 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');

और app/lib/config/config.tsमैं अपने JSON विन्यास फ़ाइल के लिए इंटरफ़ेस परिभाषित करता हूं:

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

विन्यास में संग्रहीत है assets/config/config.json:

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

नोट: मैं Build.BuildNumber सम्मिलित करने के लिए एक Azure DevOps कार्य का उपयोग करता हूं और अन्य परिनियोजन परिवेशों के लिए अन्य सेटिंग्स को प्रतिस्थापित करता हूं क्योंकि यह परिनियोजित किया जा रहा है।


2

इसके लिए यहां मेरे दो समाधान हैं

1. json फ़ाइलों में स्टोर करें

बस एक json फ़ाइल बनाएं और अपने घटक को $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किसी भी मॉड्यूल में उपयोग करें ।

बस इतना ही।


अच्छा समाधान है क्योंकि आपको पुनर्मूल्यांकन नहीं करना है। पर्यावरण कोड में इसे हार्ड-कोडिंग की तरह है। बुरा। +1
टेरेंस 00

0

मुझे यह Angular How-to: Editable config Files Microsoft Microsoft ब्लॉग से मिल रहा है जो सबसे अच्छा समाधान है। आप देव बिल्ड सेटिंग्स को कॉन्फ़िगर कर सकते हैं या सेटिंग्स का निर्माण कर सकते हैं।


0

हमारे पास यह समस्या वर्षों पहले थी जब मैं इसमें शामिल हुआ था और इसमें एक समाधान था जो उपयोगकर्ता और पर्यावरण की जानकारी के लिए स्थानीय भंडारण का उपयोग करता था। कोणीय 1.0 दिन सटीक होना चाहिए। हम पूर्व में गतिशील रूप से रनटाइम पर एक js फ़ाइल बना रहे थे जो तब उत्पन्न एपीआई यूआरएल को एक वैश्विक चर में रखेगा। हम इन दिनों थोड़ा अधिक OOP से प्रेरित हैं और किसी भी चीज़ के लिए स्थानीय भंडारण का उपयोग नहीं करते हैं।

मैंने पर्यावरण और एपि url निर्माण के निर्धारण के लिए बेहतर समाधान तैयार किया।

यह कैसे भिन्न होता है?

जब तक config.json फ़ाइल लोड नहीं होगी तब तक ऐप लोड नहीं होगा। यह एसओसी की उच्च डिग्री बनाने के लिए कारखाने के कार्यों का उपयोग करता है। मैं इसे एक सेवा में संलग्न कर सकता था, लेकिन मैंने कभी कोई कारण नहीं देखा जब फ़ाइल के विभिन्न वर्गों के बीच एकमात्र समानता यह है कि वे फ़ाइल में एक साथ मौजूद हैं। फैक्ट्री फ़ंक्शन होने से मुझे फ़ंक्शन को सीधे मॉड्यूल में पास करने की अनुमति मिलती है यदि यह फ़ंक्शन को स्वीकार करने में सक्षम है। अंत में, मेरे पास एक आसान समय है जब इंजेक्शनटोकेंस की स्थापना की जाए, जब फैक्ट्री फ़ंक्शन उपयोग के लिए उपलब्ध हों।

Downsides?

यदि आप जिस मॉड्यूल को कॉन्फ़िगर करना चाहते हैं, इस सेटअप (और अधिकांश उत्तरों) का उपयोग करके आप भाग्य से बाहर हैं, तो कारखाने के फ़ंक्शन को या तो forRoot () या forChild () में पारित करने की अनुमति नहीं देता है, और इसके लिए कोई अन्य तरीका नहीं है फ़ैक्टरी फ़ंक्शन का उपयोग करके पैकेज को कॉन्फ़िगर करें।

अनुदेश

  1. एक json फ़ाइल को पुनः प्राप्त करने के लिए लाने के लिए, मैं विंडो में ऑब्जेक्ट को स्टोर करता हूं और एक कस्टम इवेंट बढ़ाता हूं। - whatwg-fet स्थापित करने के लिए याद रखें और IE संगतता के लिए इसे अपने polyfills.ts में जोड़ें
  2. एक ईवेंट श्रोता को कस्टम ईवेंट के लिए सुनें।
  3. ईवेंट श्रोता को ईवेंट प्राप्त होता है, विंडो से ऑब्जेक्ट को एक ऑब्जर्व करने योग्य पास करने के लिए पुनः प्राप्त करता है, और यह स्पष्ट करता है कि विंडो में क्या संग्रहीत किया गया था।
  4. बूटस्ट्रैप कोणीय

- यह वह जगह है जहाँ मेरा समाधान वास्तव में भिन्न होता है -

  1. एक इंटरफ़ेस बनाने वाली फ़ाइल बनाएँ जिसकी संरचना आपके config.json का प्रतिनिधित्व करती है - यह वास्तव में प्रकार की स्थिरता के साथ मदद करता है और कोड के अगले भाग के लिए एक प्रकार की आवश्यकता होती है, और जब आप जानते हैं कि आप कुछ और अधिक निर्दिष्ट कर सकते हैं {}या निर्दिष्ट नहीं करते anyहैं
  2. BehaviorSubject बनाएं कि आप चरण 3 में पार्स किए गए जोंस फ़ाइल को पास करेंगे।
  3. SOC को बनाए रखने के लिए अपने कॉन्फ़िगरेशन के विभिन्न अनुभागों को संदर्भित करने के लिए फ़ैक्टरी फ़ंक्शन का उपयोग करें
  4. अपने कारखाने के कार्यों के परिणाम की आवश्यकता वाले प्रदाताओं के लिए InjectionTokens बनाएं

- और / या -

  1. कारखाने के कार्यों को सीधे अपने forRoot () या forChild () विधियों में से किसी एक फ़ंक्शन को स्वीकार करने में सक्षम मॉड्यूल में पास करें।

- मुख्य

मैं विंडो की जांच करता हूं ["पर्यावरण"] एक घटना श्रोता को बनाने से पहले आबाद नहीं किया जाता है ताकि समाधान के possiblilty को अनुमति दी जा सके जहां खिड़की ["पर्यावरण"] कुछ अन्य माध्यमों से पॉपअप हो जाती है।

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

--- पर्यावरण- resolvers.ts

मैं अतिरेक के लिए खिड़की ["पर्यावरण"] का उपयोग करते हुए BehaviorSubject के लिए एक मान प्रदान करता हूं। आप एक ऐसा समाधान तैयार कर सकते हैं, जहां आपका कॉन्फिगरेशन पहले से ही लोड हो और विंडो ["एनवायरनमेंट"] पहले से ही पॉपुलर हो, जब तक कि आपका कोई भी Angular का ऐप कोड रन नहीं करता, जिसमें main.ts कोड शामिल है

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 - आसान समझ के लिए नीचे स्ट्रिप किया गया

मजेदार तथ्य! NGXLogger के पुराने संस्करणों के लिए आपको LoggerModule.forRoot () में किसी ऑब्जेक्ट को पास करने की आवश्यकता है। वास्तव में, लकड़हारा अभी भी करता है! NGXLogger कृपया LoggerConfig को उजागर करता है जिसे आप सेटअप के लिए फ़ैक्टरी फ़ंक्शन का उपयोग करने की अनुमति देकर ओवरराइड कर सकते हैं।

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

परिशिष्ट

मैंने अपने एपीआई यूआरएल के निर्माण को कैसे हल किया?

मैं यह समझना चाहता था कि प्रत्येक url ने एक टिप्पणी के माध्यम से क्या किया और टाइपकास्ट करना चाहता था क्योंकि वह टाइपस्क्रिप्ट की सबसे बड़ी ताकत जावास्क्रिप्ट (IMO) की तुलना में है। मैं नए समापन बिंदु जोड़ने के लिए अन्य देवताओं के लिए एक अनुभव बनाना चाहता था, और एपिस जो संभव के रूप में सहज था।

मैंने एक वर्ग बनाया जो पर्यावरण (देव, परीक्षण, मंच, ठेस, "", और आदि) में लेता है और इस मूल्य को कक्षा की एक श्रृंखला [1-N] को पारित करता है जिसका काम प्रत्येक एपीआई संग्रह के लिए आधार यूआरएल बनाना है । प्रत्येक ApiCollection एपीआई के प्रत्येक संग्रह के लिए बेस यूआरएल बनाने के लिए जिम्मेदार है। हमारे अपने एपीआई, एक विक्रेता के एपीआई, या यहां तक ​​कि एक बाहरी लिंक हो सकता है। वह वर्ग निर्मित आधार यूआरएल को प्रत्येक बाद के एपी में सम्‍मिलित करेगा। नंगे हड्डियों का उदाहरण देखने के लिए नीचे दिए गए कोड को पढ़ें। एक बार सेटअप करने के बाद, किसी अन्य देवता के लिए बहुत आसान है कि वह किसी अन्य चीज को छूने के बिना एक और वर्ग के लिए एक एंडपॉइंट जोड़ सकता है।

TLDR; स्मृति अनुकूलन के लिए बुनियादी ओओपी सिद्धांत और आलसी गेटर्स

@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/");
  }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.