javascript - que - App.settings: ¿la forma angular?
systemjs.config.js angular 6 (5)
Quiero agregar una sección de
App Settings
en mi aplicación donde contendrá algunos consts y valores predefinidos.
Ya leí
esta respuesta
que usa
OpaqueToken
pero está en desuso en Angular.
Este
article
explica las diferencias, pero no proporcionó un ejemplo completo, y mis intentos no tuvieron éxito.
Esto es lo que he intentado (no sé si es la forma correcta):
//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'');
Y este es el componente donde quiero usar esos consts:
//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 {
}
Pero no funciona y obtengo errores.
Pregunta:
¿Cómo puedo consumir los valores de "app.settings" de forma angular?
NB Claro que puedo crear un servicio inyectable y ponerlo en el proveedor del NgModule, pero como dije, quiero hacerlo con
InjectionToken
, de forma angular.
Aquí está mi solución, cargas desde .json para permitir cambios sin reconstruir
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);
}
}
y config.json
{
"apiUrl": "http://localhost:3000/api"
}
Aquí están mis dos soluciones para esto
1. Almacenar en archivos json
Simplemente haga un archivo json y
$http.get()
su componente por el
$http.get()
.
Si necesitaba esto muy bajo, entonces es bueno y rápido.
2. Almacenar utilizando servicios de datos
Si desea almacenar y usar en todos los componentes o tiene un gran uso, es mejor usar el servicio de datos. Me gusta esto :
-
Simplemente cree una carpeta estática dentro de la carpeta
src/app
. -
Cree un archivo llamado como
fuels.ts
en la carpeta estática. Puede almacenar otros archivos estáticos aquí también. Deje definir sus datos de esta manera. Suponiendo que tenga datos de combustibles.
__
export const Fuels {
Fuel: [
{ "id": 1, "type": "A" },
{ "id": 2, "type": "B" },
{ "id": 3, "type": "C" },
{ "id": 4, "type": "D" },
];
}
- Cree un nombre de archivo static.services.ts
__
import { Injectable } from "@angular/core";
import { Fuels } from "./static/fuels";
@Injectable()
export class StaticService {
constructor() { }
getFuelData(): Fuels[] {
return Fuels;
}
}`
- Ahora puede hacer que esté disponible para cada módulo
simplemente importe en el archivo app.module.ts como este y cambie los proveedores
import { StaticService } from ''./static.services'';
providers: [StaticService]
Ahora use esto como
StaticService
en cualquier módulo.
Eso es todo.
Descubrí cómo hacer esto con InjectionTokens (vea el ejemplo a continuación), y si su proyecto fue construido usando la
Angular CLI
, puede usar los archivos de entorno que se encuentran en
/environments
para la
application wide settings
estática como un punto final de API, pero dependiendo de su proyecto requisitos que probablemente terminará usando ambos, ya que los archivos de entorno son solo literales de objeto, mientras que una configuración inyectable que usa
InjectionToken
puede usar las variables de entorno y dado que es una clase puede tener lógica aplicada para configurarlo en función de otros factores en la aplicación, como datos iniciales de solicitud http, subdominio, etc.
Ejemplo de fichas de inyección
/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 { }
Ahora puede simplemente DI en cualquier componente, servicio, etc.
/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);
}
// ...
}
Luego, también puede escribir check the config utilizando el AppConfig exportado.
No es aconsejable utilizar los archivos de
environment.*.ts
para la configuración de URL de su API.
Parece que deberías porque esto menciona la palabra "medio ambiente".
Usar esto es en realidad una configuración en tiempo de compilación . Si desea cambiar la URL de la API, deberá volver a compilar. Eso es algo que no quieres hacer ... solo pregúntale a tu amigable departamento de control de calidad :)
Lo que necesita es la configuración en tiempo de ejecución , es decir, la aplicación carga su configuración cuando se inicia.
Algunas otras respuestas tocan esto, pero la diferencia es que la configuración debe cargarse tan pronto como se inicie la aplicación , de modo que pueda ser utilizada por un servicio normal cuando lo necesite.
Para implementar la configuración de tiempo de ejecución:
-
Agregue un archivo de configuración JSON a la carpeta
/src/assets/
(para que se copie en la compilación) -
Cree un
AppConfigService
para cargar y distribuir la configuración -
Cargue la configuración usando un
APP_INITIALISER
1. Agregue el archivo de configuración a
/src/assets
Podría agregarlo a otra carpeta, pero necesitaría decirle a la CLI que es un activo en
angular.json
.
Comience usando la carpeta de activos:
{
"apiBaseUrl": "https://development.local/apiUrl"
}
2. Crear
AppConfigService
Este es el servicio que se inyectará cada vez que necesite el valor de configuración:
@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. Cargue la configuración usando un
APP_INITIALISER
Para permitir que
AppConfigService
se inyecte de forma segura, con la configuración cargada por completo, necesitamos cargar la configuración en el momento del inicio de la aplicación.
Es importante destacar que la función de fábrica de inicialización debe devolver una
Promise
para que Angular sepa esperar hasta que termine de resolverse antes de finalizar el inicio:
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 { }
Ahora puede inyectarlo donde lo necesite y toda la configuración estará lista para leer:
@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;
}
}
No puedo decirlo con suficiente fuerza, configurar sus URL de API como configuración en tiempo de compilación es un antipatrón . Usar configuración de tiempo de ejecución.
Si está utilizando angular-cli , hay otra opción:
La CLI angular proporciona archivos de entorno en
src/environments
(los predeterminados son
environment.ts
(dev) y
environment.prod.ts
(producción)).
Tenga en cuenta que debe proporcionar los parámetros de configuración en todos
environment.*
archivos de
environment.*
, Por ejemplo,
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__''
};
y úselos en su servicio (el archivo de entorno correcto se elige automáticamente):
api.service.ts
// ... other imports
import { environment } from ''../../environments/environment'';
@Injectable()
export class ApiService {
public apiRequest(): Observable<MyObject[]> {
const path = environment.apiEndpoint + `/objects`;
// ...
}
// ...
}
Lea más sobre los entornos de aplicación en Github (Angular CLI versión 6) o en la guía oficial Angular (versión 7) .