Cómo ejecutar un servicio cuando la aplicación se inicia en Angular 2

97

Creé un servicio SocketService, básicamente inicializa el socket para permitir que la aplicación escuche en el puerto. Este servicio también interactúa con algunos componentes.

// socket.service.ts

export class SocketService {
    constructor() {
        // Initializes the socket
    }
    ...
}

Sé que el código en el constructor de SocketService () solo comienza a ejecutarse cuando un componente usa SocketService.

Y, por lo general, el código en app.ts se ve así:

// app.ts

import {SocketService} from './socket.service';
...
class App {
    constructor () {}
}
bootstrap(App, [SocketService]);

Sin embargo, quiero que este servicio se ejecute cuando se inicie la aplicación. Así que hice un truco, solo agregue private _socketService: SocketServiceel constructor de la aplicación (). Así que ahora los códigos se ven así:

// app.ts (nuevo)

import {SocketService} from './socket.service';
...
class App {
    constructor (private _socketService: SocketService) {}
}
bootstrap(App, [SocketService]);

Ahora funciona. El problema es a veces que los códigos en el constructor () de SocketService se ejecutan, a veces no. Entonces, ¿cómo debo hacerlo correctamente? Gracias

Hongbo Miao
fuente
Esta guía me ha ayudado: angular.io/docs/ts/latest/tutorial/…
Marian07

Respuestas:

130

La respuesta de Stuart apunta en la dirección correcta, pero no es fácil encontrar información en APP_INITIALIZER. La versión corta es que puede usarla para ejecutar el código de inicialización antes de que se ejecute cualquier otro código de aplicación. Busqué un rato y encontré explicaciones aquí y aquí , que resumiré por si desaparecen de la web.

APP_INITIALIZER se define en angular / core. Lo incluyes en tu app.module.ts así.

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

APP_INITIALIZER es un OpaqueToken (o un InjectionToken desde Angular 4) que hace referencia al servicio ApplicationInitStatus. ApplicationInitStatus es un proveedor múltiple . Admite múltiples dependencias y puede usarlo en su lista de proveedores varias veces. Se usa así.

@NgModule({
  providers: [
    DictionaryService,
    {
      provide: APP_INITIALIZER,
      useFactory: (ds: DictionaryService) => () => return ds.load(),
      deps: [DictionaryService],
      multi: true
    }]
})
export class AppModule { }

Esta declaración de proveedor le dice a la clase ApplicationInitStatus que ejecute el método DictionaryService.load (). load () devuelve una promesa y ApplicationInitStatus bloquea el inicio de la aplicación hasta que se resuelve la promesa. La función load () se define así.

load(): Promise<any> {
  return this.dataService.getDiscardReasons()
  .toPromise()
  .then(
    data => {
      this.dictionaries.set("DISCARD_REASONS",data);
    }
  )
}

Configurado de esa manera, el diccionario se carga primero y las otras partes de la aplicación pueden depender de él de manera segura.

Editar: tenga en cuenta que esto aumentará el tiempo de carga inicial de su aplicación por el tiempo que tarde el método load (). Si desea evitar eso, puede usar un solucionador en su ruta.

GMK
fuente
Gracias por esto ... muy útil
Gaurav Joshi
5
Esta debería ser la respuesta aceptada. El actual solo mueve una línea de código de un constructor a un initmétodo. Si bien los constructores deben mantenerse lo más simples posible, ese pensamiento por sí solo no lo convierte en una solución adecuada. Usar APP_INITIALIZERhace.
JP ten Berge
No creo que la respuesta seleccionada sea incorrecta, ya que resuelve el problema de OP. PERO , como tengo un problema similar en el desarrollo de algunas bibliotecas, abrí otra pregunta donde esta respuesta encajaría perfectamente.
Machado
La mejor manera de hacerlo
Renil Babu
58

Mueva la lógica en su SocketServiceconstructor a un método en su lugar y luego llámelo en el constructor de su componente principal ongOnInit

SocketService

export class SocketService{
    init(){
        // Startup logic here
    }
}

Aplicación

import {SocketService} from './socket.service';
...
class App {
    constructor (private _socketService: SocketService) {
        _socketService.init();
    }
}
bootstrap(App, [SocketService]);
SnareChops
fuente
1
No entiendo cuál es la lógica detrás de hacer cosas en el método en lugar del constructor, ¿podría explicar esto, cuál es la ventaja de hacer lógica en el método?
Pardeep Jain
1
Un enfoque más limpio en mi humilde opinión
inoabrian
12
Los constructores deben ser lo más simples posible (normalmente solo puntos de inyección), en caso de que necesite agregar lógica adicional, use el gancho ngOnInit.
Sergio
1
Otra cosa más en la que el equipo no pensó. Cuanto más trabajo en Angular 4, me doy cuenta de lo brillantemente construido que está el framework Aurelia. Tiene todas estas posibilidades desde el primer momento simplemente agregando un decorador. Esos tipos saben lo que hacen.
Joel Hernandez
1
@CodyBugstein Depende de su caso de uso. Si es solo disparar y olvidar, simplemente llame al método async. Si necesita esperar el resultado, puede devolver un Promisede su init()método y luego encadenar según sea necesario. Cualquiera que sea el caso, se puede hacer, pero probablemente será difícil y dependerá de usted resolver los detalles. Si necesita más ayuda, siempre puede publicar una pregunta con detalles de su problema exacto y la comunidad estará encantada de ayudarlo.
SnareChops
8

También vea APP_INITIALIZER , que se describe como;

Una función que se ejecutará cuando se inicialice una aplicación.

Stuart Hallows
fuente
1

Intente crear un constructor de servicios y luego llámelo en ngOnInit () de su componente.

  • Módulo de servicio

 export class SocketService {
    constructor() { }
        getData() {
            //your code Logic
        }
}

  • Componente

export class AppComponent {
    public record;  
    constructor(private SocketService: DataService){ }
    ngOnInit() {        
        this.SocketService.getData()
        .subscribe((data:any[]) => {
            this.record = data;
        });   
  }  
}       

Espero que esto ayude.

Bhushan Gadekar
fuente
1
@Hongbo quiere que el servicio se ejecute cuando se inicie la aplicación, no en un componente en particular que esté usando el servicio
Jarod Moser
Esta respuesta realmente simple funcionó para mí. Amo las respuestas simples. Gracias.
Aggie Jon del 87 del