Angular Async Factory Provider

21.7k views Asked by At

I would like to set up a factory that does async work to return a service, and then provide that factory to a factory provider to provide that service to a component when it loads.

However, when the provider injects the TestService into the TestComponent, the type at runtime is ZoneAwarePromise. I need a way to have the provider automatically "await" the promise before it injects the service into the component.

Service

export class TestService {
 public test() {
   return 123;
 }
}

Provider and Factory

export function testFactory(auth: any, temp: any) {
  return new Promise((res, rej) => {
    res(new TestService());
  });
}

export let testProvider =
{
  provide: TestService,
  useFactory: testFactory,
  deps: []
};

App Module

providers: [
    testProvider
]

TestComponent

import { Component, OnInit } from '@angular/core';
import { TestService } from './Test';

@Component({
    selector: 'app-test'
})
export class TestComponent implements OnInit {
    constructor(private testService: TestService) { }

    async ngOnInit() {
        console.log(this.testService.test()); // fails because the type of this.testService at runtime is "ZoneAwarePromise" instead of "TestService"
    }
}
3

There are 3 answers

0
zos1000 On

you can make your promise function to be async

export function testFactory(auth: any, temp: any) {
  return new Promise(async(res, rej) => {
    const inst = new TestService();
    await inst.ngOnInit();
    res(inst);
  });
}

export let testProvider =
{
  provide: TestService,
  useFactory: testFactory,
  deps: []
};
0
Ambrus Tóth On

I have a service ConfigService, which I want to load asynchronously:

My providers array in app.module.ts:

providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: ConfigService.factory,
      deps: [HttpClient, ConfigService],
      multi: true
    }
  ]

The async-loaded service (config.service.ts):

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ConfigService {
  constructor(private http: HttpClient) {
  }

  async init() {
    // await http request
  }

  static factory(http: HttpClient, configService: ConfigService) {
    return () => configService.init();
  }
}

0
Huan On

It seems Angular cannot implement the async factory function for the provider directly.

In order to do this, we need to set up a new function and hand it over to the NgModule to do the APP_INITIALIZER job.

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

function configFactory(testService: TestService) {
  // do the async tasks at here
  return () => testService.init()
}

@NgModule({
  providers: [
    {
      provide:      APP_INITIALIZER,
      useFactory:   configFactory,
      deps:         [TestService],
      multi:        true,
    },
  ],
})

See Also

Angular4 APP_INITIALIZER won't delay initialization