2

first off, we probably have an unusual angular2 architecture where we bootstrap multiple components (widgets) on a single page and not a single app-component that handles everything.

My first question is, whether the following two implementations are the same, from what I understood, they are (please correct me if I misunderstood something):

 bootstrap(SomeComponent, [HTTP_PROVIDER, FooService, ServiceNeededByFoo])

This initializes SomeComponent and puts the providers for the three services in there. This way SomeComponent can resolve FooService in its own constructor, as well as FooService can resolve ServiceNeededByFoo since it looks it up in SomeComponent (the parent).

If I define

providers: [HTTP_PROVIDER, FooService, ServiceNeededByFoo]

inside SomeComponent, I can initialize it like so:

bootstrap(SomeComponent)

So from what I understand these should be completely the same, a new instance for FooService and ServiceNeededByFoo on the level of SomeComponent is created and shared with all children of SomeComponent that also need FooService. If it's the same, is there any preferred/recommended way?

The second question is now, how can I share a single instance of FooService across components that are not in the same DI hierarchy:

bootstrap(SomeComponent1, FooService)
bootstrap(SomeComponent2, FooService)

where the FooService should be the same instance. Something like:

var foo = new FooService();
foo.expensiveInit();
bootstrap(SomeComponent1, provide(FooService, {instance: foo}))
bootstrap(SomeComponent2, provide(FooService, {instance: foo}))
Tom
  • 3,807
  • 4
  • 33
  • 58

2 Answers2

2

Angular DI creates a hierarchy of injectors where the root is created by bootstrap() and the providers passed to bootstrap().
All stuff provided by Angular by default is added to this injector as well (like PLATFORM_PIPES, PLATFORM_DIRECTIVES, ...)

Then starting from the root component a child injector is created with the providers provided at the root component. For each child component and directive of the root component, child injectors are created, down to the leaf components and directives.

The injector hierarchy therefore resembles your component and directive hierarchy.

When now a component requires a dependency (constructor parameter), DI looks it up on it's injectors providers. If it can't find it, it checks the parent injectors, and that goes on up to the root injector created by bootstrap().

This should make it pretty clear that providers at bootstrap() or providers at root component result in quite the same behavior.

Your approach to share a service between two individually bootstrapped components seems fine to me, except that AFAIK it should be useValue instead of instance

bootstrap(SomeComponent1, provide(FooService, {useValue: foo}))

See also

As mentioned there, if you emit values using an observable in the shared service, the value is emitted in the senders zone and subscribers in the other application are called in the senders zone. Ensure you use zone.run

constructor(sharedService:FooService, zone:NgZone) {
  sharedService.someObservable.subscribe(value => {
    zone.run(() => {
      this.data = value;
      this.router.navigate...
      ...
    });
  });
}
Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
1

Just for reference if someone stumbles upon this post and also has some problems with shared instances when there is no central app component.

My final implementation looks like this, it's an example with a service that needs injected services itself and a simple service:

var cs = new ConfigurationService();
var injector = ReflectiveInjector.resolveAndCreate([TranslateService, provide(TranslateLoader, {useClass: JsonLoader}), HTTP_PROVIDERS]);
var ts = injector.get(TranslateService);

ts.setDefaultLang('en');
ts.use(cs.getLanguage());

var defaultProviders = [provide(TranslateService, {useValue: ts}), provide(ConfigurationService, {useValue: cs})];

if ($('notification-widget').length > 0) {
   bootstrap(NotificationWidgetComponent, defaultProviders);
}

if ($('livesearch-widget').length > 0){
   bootstrap(LivesearchWidgetComponent, defaultProviders);
}
Tom
  • 3,807
  • 4
  • 33
  • 58
  • The provide mehods and the Provider class are now depricates as of rc3, I haven't found the replacement yet since the comments in the code simple say "TODO: improve docs" – Tom Jun 27 '16 at 11:34