4

I have a provider in AppModule which provides a class.

Is there a way to update the provided value at runtime?

{provide: MatDatepickerIntl, useClass: SomeClass}

How can replace SomeClass with AnotherClass at runtime (i.e.: when reacting to an event in a component).

I'm using Angular 9.

EDIT

I know could use useFactory to generate the provided value based on some logic. The problem with that approach is that the factory function still runs just once, when the component is created.

What I wanna do is run that function again whenever some event occurs. How can I do that?

EDIT2:

So app.module does this:

// this is in providers array...
     {
          provide: MatDatepickerIntl, deps: [DatePickerIntlService],
          useFactory: (datePickerIntl) => datePickerIntl.getLocale()
     }

The above service reads current locale and it returns a MatDatepickerIntl subclass instance accordingly, so it creates the correct class.

Finally I have the event in app component ngOnInit:

onLangChange.subscribe(() => {
          
        })

How do I change the provided MatDatepickerIntl subclass in the event handler? I can call the service but there is no way to update the provided value with the result...

Material Date Picker expects MatDatepickerIntl token to return the desired subclass. That is how it loads localized texts.

Hope this clarifies things a bit better.

Thanks in advance!

user2297996
  • 1,382
  • 3
  • 18
  • 28
  • this might help => https://stackoverflow.com/questions/34712171/how-to-add-providers-to-injector-dynamically – micronyks Aug 06 '20 at 18:19
  • this one also => . https://www.damirscorner.com/blog/posts/20170526-DynamicDependencyInjectionInAngular.html – micronyks Aug 06 '20 at 18:24
  • @micronyks Thanks for the info, please see my edit above. Problem is these run only when decorators run. I need to re-execute the provider later, from component ngOnInit method. – user2297996 Aug 06 '20 at 19:49
  • Your requirement is to replace this provider in all your application or just in one component? – Raz Ronen Aug 06 '20 at 20:11
  • @RazRonen All over the app. This is provided in root (app module). – user2297996 Aug 06 '20 at 21:18
  • Not sure you can do that, it’ll mean to go to all those classes that already injected it and switch their property. Can you consider wrapping the two services (the initial one and the runtime) in another service and have give him responsibility to make the switch? I don’t see any problems there. Will be happy to help with this solution. – Raz Ronen Aug 07 '20 at 06:20
  • @RazRonen Thx for the help! I am happy to try anything, but I do not fully understand what do you mean. I have to provide a class as MatDatepickerIntl because that is a requirement of Angular Material. I can put some logic into a service for sure, but that service will still run only once (when provided). WHat is the proper way to do this? – user2297996 Aug 07 '20 at 07:57

1 Answers1

0

You have to make a parent Service, which will reroute to the expected service:

@Injectable({
  providedIn: 'root'
})
export class MyParentService {
  childService: ChildEnService | ChildFrService | ChildEsService;
  private ngDestroy$: Subject<void> = new Subject();

  constructor(private childEnService: ChildEnService,
              private childFrService: ChildFrService,
              private childEsService: ChildEsService,
              private languageObsService: LanguageObsService) {
    this.languageObsService.getCurrentLanguage().pipe(
                                                 takeUntil(this.ngDestroy$)
                                               ).subscribe(
                                                 newLanguage => this.updateChild(newLanguage);
                                               );
  }

  public getMyPreciousData() {
    return this.childService?.getMyPreciousData();
  }

  private updateChild(newLanguage: LanguageEnum) {
    swtich(newLanguage) {
      case LanguageEnum.en:
        this.childService = this.childEnService;
        break;
      case LanguageEnum.fr:
        this.childService = this.childFrService;
        break;
      case LanguageEnum.es:
        this.childService = this.childEsService;
        break;
    }
  }

  private ngOnDestroy() {
    // unsubscribe the languageObsService
    this.ngDestroy$.next();
    this.ngDestroy$.complete();
  }
}


@Injectable({
  providedIn: 'root'
})
export class LanguageObsService{
  currentLanguage$: BehaviorSubject<LanguageEnum> = new BehaviorSubject(LanguageEnum.en);

  constructor() {}

  public getCurrentLanguage(): Observable<LanguageEnum> {
    return this.currentLanguage$.asObservable();
  }

  public setCurrentLanguage(newLanguage: LanguageEnum): void {
    this.currentLanguage$.next(newLanguage);
  }
}
Random
  • 3,158
  • 1
  • 15
  • 25
  • But how do I make this service run again later, when an event occurs in the component? I already have something like this, but it runs only at provider timer. – user2297996 Aug 07 '20 at 09:03
  • `getServiceToUse` will be run every time you USE the service, so you have to put your condition inside this method. Can you tell more about how you know which service to use ? – Random Aug 07 '20 at 09:05
  • Yeah so whenever a language change occurs in the app (implemented via ngx-translate), I need to update the MatDatepickerIntl. It is class which contains localisation data. But there is a subclass for each language. SO when language changes, the class reference also has to change. – user2297996 Aug 07 '20 at 09:14
  • How do you change language ? Is there a button somewhere to switch ? Then you could use an ObservableService. I'll update my answer this way. – Random Aug 07 '20 at 09:19
  • Yes there are buttons. There is even an language change event which is an observable. But when that happens, how do I tell MatDatepickerIntl to now point to MatDatepickerIntlSubClassEnglish? – user2297996 Aug 07 '20 at 09:22
  • If you have an event, you don't even need the ObsService, just subscribe to the event instead of the obsService. – Random Aug 07 '20 at 10:21
  • But what that event should do? How should it update the provided value? Please see my edit above. – user2297996 Aug 07 '20 at 11:04