24

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"
    }
}
2

3 Answers 3

5

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

Sign up to request clarification or add additional context in comments.

Comments

2

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();
  }
}

Comments

-1

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: []
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.