typescript angular

typescript - bootstrap angular2 con datos de llamadas ajax



(3)

Quiero iniciar mi aplicación con los datos que estoy recuperando de un servicio. Estoy haciendo algo en la línea de

let dependencies = [ //... a load of dependencies MyService ]; let injector = Injector.resolveAndCreate(dependencies); let service: MyService = injector.get(MyService); service.getData() // returns observable .toPromise() .then((d) => { // use data to append to dependencies bootstrap(App, dependencies) });

Esto funciona bien, pero no me gusta usar la matriz de dependencias dos veces, ¿hay alguna forma más limpia de hacerlo? ¿Puedo agregar cosas al inyector de la aplicación después de bootstrap? También noto que la función bootstrap devuelve una promesa, ¿puedo usar esta promesa para evitar el bootstrap de la aplicación hasta que finalice mi solicitud de ajax?

Por supuesto, para el Injector , podría usar solo las dependencias requeridas por MyService pero esto lo hace muy frágil, como se puede imaginar.


@Thierry (como siempre) ha respondido bien el corazón de su pregunta, pero creo que vale la pena señalarlo por separado:

¿Puedo agregar cosas al inyector de la aplicación después de bootstrap?

Sí, declarándolos en providers o viewProviders en los decoradores de los componentes que los requieren. p.ej:

//main.ts bootstrap(MyComponent) //no dependencies declared //my.service.ts @Injectable class MyService { public getMessage = () => "foobar" } //my.component.ts @Component({ selector: ''foo'', providers: [MyService] template: `<div>{{mySvc.getMessage()}}</div>` //displays foobar }) class MyComponent { constructor(private mySvc: MyService){ } }

Tenga en cuenta que los providers se pueden usar tanto en directivas como en componentes (es una opción en viewProviders , desde la que se extiende ComponentMetadata ), mientras que viewProviders solo está disponible en componentes por razones que son claras dada la diferencia entre ellos .

En mi humilde opinión, es una buena práctica inyectar dependencias de esta manera siempre que sea posible en lugar de hacerlo de forma bootstrap , ya que le permite limitar el alcance de disponibilidad de una dependencia dada a la parte de la aplicación (es decir, sub-árbol de componentes) donde desee Que esté disponible. También es propicio para la carga progresiva y evita el olor a SoC de configurar innumerables inyectables no relacionados en un solo archivo de arranque.


El problema aquí es que Angular2 no le da acceso a la referencia de la aplicación y su inyector antes de iniciar el componente principal. Vea esta línea en el código fuente: https://github.com/angular/angular/blob/master/modules/angular2/platform/browser.ts#L110 .

Un enfoque podría ser implementar un bootstrap personalizado en lugar de usar el predeterminado. Algo así que divide la creación de la aplicación y el boostrapping en el componente de la aplicación. De esta manera, podrá cargar algo entre las dos tareas.

Aquí hay una implementación de muestra:

function customBoostrap(appComponentType, customProviders) { reflector.reflectionCapabilities = new ReflectionCapabilities(); let appProviders = isPresent(customProviders) ? [BROWSER_APP_PROVIDERS, customProviders] : BROWSER_APP_PROVIDERS; var app = platform(BROWSER_PROVIDERS).application(appProviders); var service = app.injector.get(CompaniesService); return service.getCompanies().flatMap((companies) => { var companiesProvider = new Provider(''companies'', { useValue: data }); return app.bootstrap(appComponentType, [ companiesProvider ]); }).toPromise(); }

y úsalo de esta manera:

customBoostrap(AppComponent, [ HTTP_PROVIDERS, CompaniesService ]);

Las empresas estarán disponibles automáticamente para inyección dentro del componente, por ejemplo:

@Component({ (...) }) export class AppComponent { constructor(@Inject(''companies'') companies) { console.log(companies); } }

Consulte este plunkr correspondiente: https://plnkr.co/edit/RbBrQ7KOMoFVNU2ZG5jM?p=preview .

En este momento, es un poco extraño, pero este enfoque podría proponerse como una solicitud de función ...

Editar

Después de echar un vistazo al documento de la clase ApplicationRef , vi que hay una solución más simple ;-)

var app = platform(BROWSER_PROVIDERS) .application([BROWSER_APP_PROVIDERS, appProviders]); service.getCompanies().flatMap((companies) => { var companiesProvider = new Provider(''companies'', { useValue: data }); return app.bootstrap(appComponentType, [ companiesProvider ]); }).toPromise();

Aquí está el plunkr correspondiente: https://plnkr.co/edit/ooMNzEw2ptWrumwAX5zP?p=preview .


También puede usar el APP_INITIALIZER inyección APP_INITIALIZER y también puede hacer que llame a múltiples recursos asíncronos en paralelo:

import { NgModule, APP_INITIALIZER } from ''@angular/core''; import { HttpClientModule } from "@angular/common/http"; import { AppLoadService } from ''./app-load.service''; export function init_app(appLoadService: AppLoadService) { return () => appLoadService.initializeApp(); } export function get_settings(appLoadService: AppLoadService) { return () => appLoadService.getSettings(); } @NgModule({ imports: [HttpClientModule], providers: [ AppLoadService, { provide: APP_INITIALIZER, useFactory: init_app, deps: [AppLoadService], multi: true }, { provide: APP_INITIALIZER, useFactory: get_settings, deps: [AppLoadService], multi: true } ] }) export class AppLoadModule { }

Fuente: Tutorial de Angular 4 - Ejecutar código durante la inicialización de la aplicación

Otro artículo interesante: enganche en el proceso de inicialización de Angular