navigationend example change typescript dependency-injection angular

typescript - example - Angular 2: Servicio de inyección en clase.



router events angular 6 (4)

A partir de Angular 5.x:

import { Injector } from "@angular/core"; export class Model { static api: Api; constructor(data: any) { // check the api ref not exist // We don''t want to initiate a new object every time if (!Model.api){ //try inject my api service which use the HttpClient const injector: any = Injector.create([{ provide: Api, useClass: Api, deps: [] }]); Model.api = injector.get(Api); } // ..... } }

Tengo clase angular que representa la forma. Quiero poder crear instancias múltiples de esa clase usando el constructor.

El constructor toma múltiples argumentos que representan las propiedades de esa forma.

constructor(public center: Point, public radius: number, fillColor: string, fillOpacity: number, strokeColor: string, strokeOpacity: number, zIndex: number)

Dentro de mi clase quiero usar un servicio que proporcione la capacidad de dibujar formas en el mapa. ¿Es posible inyectar ese servicio en mi clase y seguir usando el constructor de manera estándar?

Así que quiero hacer algo como lo siguiente y hacer que Angular resuelva automáticamente la dependencia inyectada.

constructor(public center: GeoPoint, public radius: number, fillColor: string, fillOpacity: number, strokeColor: string, strokeOpacity: number, zIndex: number, @Inject(DrawingService) drawingService: DrawingService)


Aquí hay otras dos formas posibles de lograr el resultado deseado o muy similar.

Primer enfoque: utilizar un administrador para sus entidades u objetos que no sean de servicio

Tiene uno o más servicios de fábrica que están a cargo de la creación de instancias de sus objetos.

Esto significa que puede proporcionar los depósitos necesarios más allá de los objetos, y no requiere que se los pase usted mismo.

Por ejemplo, digamos que tienes entidades como una jerarquía de clases:

abstract class Entity { } class SomeEntity extends Entity { ... }

Luego puede tener un EntityManager que es un servicio y puede construir entidades:

@Injectable() // is a normal service, so DI is standard class EntityManager { constructor(public http: Http) { } // you can inject any services now create<E extends Entity>(entityType: { new(): E; }): E { const entity = new entityType(); // create a new object of that type entity.manager = this; // set itself on the object so that that object can access the injected services like http - one can also just pass the services not the manager itself return entity; } }

También puede tener parámetros de construcción si lo desea (pero no tendrán ningún tipo de información porque la create debe funcionar con todos los tipos de entidades):

class SomeEntity extends Entity { constructor(param1, param1) { ... } } // in EntityManager create<E extends Entity>(entityType: { new(): E; }, ...params): E { const entity = new entityType(...params); ... }

Sus entidades ahora pueden declarar al administrador:

abstract class Entity { manager: EntityManager; }

Y tus entidades pueden usarlo para hacer lo que sea:

class SomeEntity extends Entity { doSomething() { this.manager.http.request(''...''); } }

Ahora, cada vez que necesite crear una entidad / objeto, use este administrador. El EntityManager debe inyectarse pero las entidades son objetos libres. Pero todo el código angular se iniciará desde un controlador o servicio, de modo que sea posible inyectar el administrador.

// service, controller, pipe, or any other angular-world code constructor(private entityManager: EntityManager) { this.entity = entityManager.create(SomeEntity); }

Este enfoque también puede adaptarse a objetos arbitrarios. No necesita una jerarquía de clases, pero con escritura de caracteres esto funciona mejor. También tiene sentido tener alguna clase base para sus objetos, ya que también puede reutilizar el código de esta manera antigua, especialmente en un enfoque orientado a dominios / objetos.

PROS : este enfoque es más seguro porque aún reside en la jerarquía DI completa y debería haber menos efectos secundarios no deseados.

CONTRAS : El inconveniente es que nunca puede usar new nunca más, ni puede obtener acceso a estos servicios en código arbitrario. Siempre debe confiar en el DI y en su fábrica / fábricas.

Segundo enfoque - h4ckz0rs

Usted crea un servicio dedicado a obtener (a través de DI) los servicios que necesita en los objetos.

Esta parte es muy similar al primer enfoque, solo que este servicio no es una fábrica. En su lugar, pasa los servicios inyectados a un objeto que se define fuera de esta clase en un archivo diferente (explicación posterior). Por ejemplo:

... import { externalServices } from ''./external-services''; @Injectable() export class ExternalServicesService { constructor(http: Http, router: Router, someService: SomeService, ...) { externalServices.http = http; externalServices.router = router; externalServices.someService = someService; } }

El objeto que contendrá los servicios se define en su propio archivo como tal:

export const externalServices: { http, router, someService } = { } as any;

Tenga en cuenta que los servicios no utilizan ningún tipo de información (esto es un inconveniente pero es necesario).

Luego, debe asegurarse de que ExternalServicesService se inyecte una vez. El mejor lugar es usar el componente principal de la aplicación:

export class AppComponent { constructor(..., externalServicesService: ExternalServicesService) {

Finalmente, ahora puede usar los servicios en cualquier objeto arbitrario en cualquier momento después de que se haya instanciado el componente principal de la aplicación.

import { externalServices } from ''../common/externalServices'' // or wherever is defined export class SomeObject() { doSomething() { externalServices.http().request(...) // note this will be called after ng2 app is ready for sure } }

Tenga en cuenta que no podrá llamar a ninguno de estos servicios en el código de clase o en objetos no instanciados después de que se haya creado una instancia de la aplicación. Pero en una aplicación típica, esto nunca debería ser necesario.

Ahora, algunas explicaciones sobre esta configuración extraña:

¿Por qué usar un objeto externalServices en un archivo separado en lugar del mismo archivo o simplemente guardar los servicios en la propia clase (como atributos estáticos) y por qué se desvinculan los servicios?

La razón es que cuando está compilando el código, por ejemplo, a través de angular-cli / webpack con el modo --prod , es muy probable que obtenga dependencias cíclicas que no se pueden resolver correctamente y obtendrá errores feos que son difíciles de encontrar. Ya he pasado por esto :).

Un error del formulario.

No se puede leer la propiedad ''prototipo'' de indefinido

visto solo cuando se ejecuta con el indicador --prod indicará que las dependencias no se resuelven correctamente.

Es mucho mejor asegurarse de que ExternalServicesService solo dependa de externalServices para pasar las instancias de servicio, y que la aplicación solo inyecte ExternalServicesService una vez (por ejemplo, en su AppComponent principal), entonces todos los códigos / objetos arbitrarios solo usarán externalServices para obtener los servicios.

Por lo tanto, cualquier código de este tipo solo necesitará importar los servicios externalServices que no tienen más deps (porque los servicios tampoco están escritos). Si hubieran importado ExternalServicesService , habría importado todo lo demás y no habría podido resolver el departamento bidireccional de forma estática. Y esto se convierte en un problema importante en ng2 / webpack al agrupar para prod.

Lo mismo sucedería si tuviéramos que utilizar tipos para los servicios, ya que eso requerirá imports .

PROS : este enfoque es más fácil de usar una vez que se ha realizado la configuración, y usted es libre de usar el new . Básicamente, cualquier archivo de código puede importar los servicios externalServices y tener acceso instantáneo a los servicios que desea exponer de esta manera.

CONTRAS: El inconveniente es la configuración hackish y los posibles problemas causados ​​por deps cíclicos. También es más sensible, ya que no puede estar seguro de que externalServices tenga esos servicios de inmediato. Solo se definirán una vez que se inicie la aplicación ng2 y se inyecte por primera vez el ExternalServicesService . Un inconveniente también es que ya no tiene información de tipo sobre esos servicios.

PD: No estoy seguro de por qué este tema no es más popular.

Por ejemplo, ser un fanático del diseño orientado al dominio, tener entidades poderosas (por ejemplo, con métodos dirigidos a llamadas REST o interactuar con otros servicios) es importante y esta limitación siempre lo hizo difícil.

Tuvimos que superar esta limitación tanto en angularjs como ahora en Angular2 +, ya que parece que aún no se ha abordado en la biblioteca.


De hecho, no puedes. La clase debe estar decorada con @Injectable para permitir que Angular2 inyecte cosas. El decorador @Inject está "solo" allí para especificar metadatos adicionales sobre qué inyectar.

En su caso, usted administra la clase, ya que la mayoría de los parámetros de su constructor no corresponden a dependencias y se proporcionan cuando se crea una instancia explícita de la clase.


He logrado resolver mi problema.

Angular 2 - 4 proporciona un inyector reflectivo que permite inyectar dependencias fuera de los parámetros del constructor.

Todo lo que tenía que hacer era importar el inyector reflectivo de @angular/core .

import {ReflectiveInjector} from ''@angular/core'';

Y entonces:

let injector = ReflectiveInjector.resolveAndCreate([DrawingService]); this.drawingApi = injector.get(DrawingService);

La clase ni siquiera tiene que ser decorada con el decorador @Injectable . El único problema es que tengo que proporcionar todas las dependencias para DrawingService y todas las dependencias anidadas, por lo que es difícil de mantener.

EDITAR :

Angular 5

import { Injector } from "@angular/core"; const injector = Injector.create([ { provide: DrawingService } ]); this.drawingApi = injector.get(DrawingService);

Angular 6

import { Injector } from "@angular/core"; const injector = Injector.create({ providers: [ { provide: DrawingService }, ] }); this.drawingApi = injector.get(DrawingService);