-1

I would like to resolve a configuration object for the injector when ngOnInit is called.

This is what I am trying to do:

<my-component [config]="someConfig"></my-component>
// my.component.ts

import { CONFIG_TOKEN } from 'injector_tokens';

@Component({
  selector: 'my-component',
  template: '<p>need to delay dependency injector resolution... but how?! it's not in the docs...</p>',
  styleUrls: [],
  providers: [
    SomeService
  ]
})
export class MyComponent implements OnInit {
  @Input() config: Config;
  ngOnInit(): void {
    // I want to be able to now add to the injector's providers array: 
    // { provide: CONFIG_TOKEN, useValue: this.config }
    // how can I do that?
  }

}
// some_service.ts

import { CONFIG_TOKEN } from 'injector_tokens';

export class SomeService {
  constructor(@Inject(CONFIG_TOKEN) config: Config) {
    // config should be the config object passed in via html attribute
  }
}

How can I do it?

patrick
  • 9,290
  • 13
  • 61
  • 112
  • It's not in the docs because there's no mechanism for that. You can inject the injector, then ask it for an instance of a given dependency, but at a certain point the level of inherent coupling means you're not benefiting from DI - what's the actual _context_ here? – jonrsharpe Mar 17 '22 at 23:11
  • I’m wondering the same. Context. There is also a runtime versus build time problem here, things are statically analyzed by the compiler here (like the providers array). – MikeOne Mar 17 '22 at 23:14
  • @jonrsharpe dynamic configuration object passed in via template `` being available to all services used by the component, without having to manually new up said services in component.. Seems like a pretty reasonable thing that should be possible... – patrick Mar 17 '22 at 23:35
  • @jonrsharpe I edited the code in my question to add a little more context... – patrick Mar 17 '22 at 23:38
  • 1
    Does it? If you don't have the value until after the component is created then you can't create the services so you can't create the component. – jonrsharpe Mar 17 '22 at 23:40
  • Is this a duplicate of https://stackoverflow.com/questions/35267146/accessing-a-property-in-a-parent-component? – meriton Mar 18 '22 at 00:24

2 Answers2

0

I see an issue from the get go.

<my-component config="someConfig"></my-component>

this would bind the string "someConfig"

It should be:

 <my-component [config]="someConfig"></my-component>

this would bind the variable someConfig from the holding component component.

for the injection stuff. I dont see you need injection if you have someConfig available already but just passes it into my-component

Edit: For the service you have 3 levels you can provide it. On Root, Module and Component level

for modules and components its done the same way.

@Component({
    selecter : 'my-component',
    ...
    providers: [ MyService ]
})
export class MyComponent { ... }

Now you might want to use a factory or something like that to parse the extra value. Or you could have create a injection token which you set in the providers instead. relevant source for how to: https://angular.io/guide/dependency-injection-providers

  • yes, the missing square brackets was a typo (i updated my code in the question)... I don't understand your last sentence. How can someConfig be made available to services used within the component without manually newing up the services or manually setting the config as properties on them. – patrick Mar 18 '22 at 05:15
  • You can provide the service on component level as well. I updated the answer. There is a couple of ways of doing it, but you should check the link i added, what fits you needs the best. – Henrik Bøgelund Lavstsen Mar 18 '22 at 07:18
  • No, it doesn't, AFAICT. The problem is that the config is filled _after_ construction, so _after_ the dependency injection does its job. – Alberto Chiesa Mar 18 '22 at 07:31
  • @A.Chiesa yeah if that is the case it should probably just be a setter for the service. – Henrik Bøgelund Lavstsen Mar 18 '22 at 07:39
0

Well, it' usually an anti-pattern, but I see the use. Basically you have a config which comes to the component via template, but the config is also for the service instance underlying the component's business logic.

Seems fair.

I think your best bet is to trade the construction-time usability of your service (usually a nice characteristic) with an Init(config: Config) method, to be called inside the ngOnInit method of your component. In this way, you keep the Dependency Injection provided parameters in the service constructor, you keep the providers definition in your component easy peasy, and there is no direct usage of the injector.

If you want, you could even call the SomeService.Init method in the setter of the component's input property:

@Component({
  selector: 'my-component',
  template: '<p>whatever...</p>',
  styleUrls: [],
  providers: [ SomeService ]
})
export class MyComponent {

  private _config: Config = null;
  @Input() get config(): Config {
    return _config;
  }
  set config(v: Config) {
    _config = v;
    this.someService.Init(v);
  }

  constructor(Self() private someService: SomeService) {
  }
}
Alberto Chiesa
  • 7,022
  • 2
  • 26
  • 53