28

I have an object that I want to share between my components into an Angular2 app.

Here is the source of the first component:

/* app.component.ts */

// ...imports
import {ConfigService} from './config.service';

@Component({
    selector: 'my-app',
    templateUrl: 'app/templates/app.html',
    directives: [Grid],
    providers: [ConfigService]
})
export class AppComponent {
    public size: number;
    public square: number;

    constructor(_configService: ConfigService) {
        this.size = 16;
        this.square = Math.sqrt(this.size);

        // Here I call the service to put my data
        _configService.setOption('size', this.size);
        _configService.setOption('square', this.square);
    }
}

and the second component:

/* grid.component.ts */

// ...imports
import {ConfigService} from './config.service';

@Component({
    selector: 'grid',
    templateUrl: 'app/templates/grid.html',
    providers: [ConfigService]
})
export class Grid {
    public config;
    public header = [];

    constructor(_configService: ConfigService) {
        // issue is here, the _configService.getConfig() get an empty object 
        // but I had filled it just before
        this.config = _configService.getConfig();
    }
  }

and finally my little service, the ConfigService:

/* config.service.ts */

import {Injectable} from 'angular2/core';

@Injectable()
export class ConfigService {

    private config = {};

    setOption(option, value) {
        this.config[option] = value;
    }

    getConfig() {
        return this.config;
    }
}

My data are not shared, in the grid.component.ts, the _configService.getConfig() line return an empty object, but it is filled just before in the app.component.ts.

I read the docs and tutorials, nothing worked.

What am I missing ?

Thanks

SOLVED

My issue was that I was injecting my ConfigService twice. In the bootstrap of the application and in the file where I'm using it.

I removed the providers setting and its worked !

Łukasz
  • 8,555
  • 2
  • 28
  • 51
keversc
  • 346
  • 1
  • 3
  • 9

3 Answers3

28

You define it within your two components. So the service isn't shared. You have one instance for the AppComponent component and another one for the Grid component.

@Component({
  selector: 'my-app',
  templateUrl: 'app/templates/app.html',
  directives: [Grid],
  providers: [ConfigService]
})
export class AppComponent {
  (...)
}

The quick solution is to remove the providers attribute to your Grid component... This way the service instance will be shared by the AppComponent and its children components.

The other solution is to register the corresponding provider within the bootstrap function. In this case, the instance will be shared by the whole application.

bootstrap(AppComponent, [ ConfigService ]);

To understand why you need to do that, you need to be aware of the "hierarchical injectors" feature of Angular2. Following links could be useful:

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • OK, in fact I have both... In the bootstrap and in providers in each file i want. This can be my issue so. I test it asap and tell you if this was the problem – keversc Feb 08 '16 at 15:27
  • Yes, but you don't need this. When you configure your providers, the scope of the associated instance will be the element and its sub elements. Defining again will recreate a new instance. For example if you define your provider in bootstrap and one in a component you will have two instances of the service: one at the application level and one at the component level. – Thierry Templier Feb 08 '16 at 15:31
  • If you define only at the application level, all components could use this instance by dependency injection. It's because the injector of the component is a sub injector or the application one. They leverage prototypal inheritance. The child can see elements in the parent – Thierry Templier Feb 08 '16 at 15:32
  • Ok, thanks for the links and explanations, I need to know more from services before continue ! – keversc Feb 08 '16 at 15:34
  • You're welcome! In fact the important issue here is the hierachical injectors ;-) If you define twice, you don't use the provider of the parent anymore... – Thierry Templier Feb 08 '16 at 15:35
  • Coming back to you, it was my issue. It solved now. Thank you again – keversc Feb 09 '16 at 14:15
  • Thanks @ThierryTemplier - this is exactly what I was looking for. What about siblings, though? Do they just both share the parent service? – itamar Aug 10 '16 at 15:38
  • You're welcome! If you have the service provider in the parent component, all children will share this service. Otherwise no. It's because of hierarchical injectors of Angular2... – Thierry Templier Aug 10 '16 at 15:41
  • Perhaps this question could interest you: http://stackoverflow.com/questions/34804298/whats-the-best-way-to-inject-one-service-into-another-in-angular-2-beta/34807397 – Thierry Templier Aug 10 '16 at 15:41
  • Where do you import the bootstrap function from? I can't seem to find it in the docs – avoliva Dec 29 '16 at 21:12
  • You are a modern day hero – Sam Alexander May 22 '17 at 19:35
6

For the latest version of angular, if you want to share the service, you can't add it to the bootstrap function. Just add it to the NgModule providers list as you would do with a normal service, its default behaviour will be singleton.

bootstrap(AppComponent);

@NgModule({
    declarations: [
        ....
    ],
    imports: [
       ....     
    ],
    providers: [
        ConfigService,
....
Mike D
  • 220
  • 1
  • 4
  • 12
4

Don't add ConfigService to providers of your component. This results in new instances for every component. Add it to providers of a common parent component. If you add it to your root component or bootstrap(App, [ConfigService]) your entire application shares a single instance.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • OK thanks, I will look at this, for now I have the ConfigService in both the bootstrap and the file, this may be my issue. I try this asap – keversc Feb 08 '16 at 15:28