How do I share data between components in Angular 2?

Solution 1:

A service singleton is a nice solution. Other way - data/events bindings.

Here is an example of both:

class BazService{
  n: number = 0;
  inc(){
    this.n++;
  }
}

@Component({
  selector: 'foo'
})
@View({
  template: `<button (click)="foobaz.inc()">Foo {{ foobaz.n }}</button>`
})
class FooComponent{
  constructor(foobaz: BazService){
    this.foobaz = foobaz;
  }
}

@Component({
  selector: 'bar',
  properties: ['prop']
})
@View({
  template: `<button (click)="barbaz.inc()">Bar {{ barbaz.n }}, Foo {{ prop.foobaz.n }}</button>`
})
class BarComponent{
  constructor(barbaz: BazService){
    this.barbaz = barbaz;
  }
}

@Component({
    selector: 'app',
    viewInjector: [BazService]
})
@View({
  template: `
    <foo #f></foo>
    <bar [prop]="f"></bar>
  `,
  directives: [FooComponent, BarComponent]
})
class AppComponent{}

bootstrap(AppComponent);

Watch live

Solution 2:

The comment by @maufarinelli deserves its own answer because until I saw it, I was still bashing my head against the wall with this issue even with @Alexander Ermolov's answer.

The problem is that when you add a providers to your component:

@Component({
    selector: 'my-selector',
    providers: [MyService],
    template: `<div>stuff</div>`
})

This causes a new instance of your service to be injected... rather than being a singleton.

So remove all instances of your providers: [MyService] in your application except in the module, and it will work!

Solution 3:

You must use inputs and outputs of a @Component decorator. Here is the most basic example of using both;

import { bootstrap } from 'angular2/platform/browser';
import { Component, EventEmitter } from 'angular2/core';
import { NgFor } from 'angular2/common';

@Component({
  selector: 'sub-component',
  inputs: ['items'],
  outputs: ['onItemSelected'],
  directives: [NgFor],
  template: `
    <div class="item" *ngFor="#item of items; #i = index">
      <span>{{ item }}</span>
      <button type="button" (click)="select(i)">Select</button>
    </div>
  `
})

class SubComponent {
  onItemSelected: EventEmitter<string>;
  items: string[];

  constructor() {
    this.onItemSelected = new EventEmitter();
  }

  select(i) {
    this.onItemSelected.emit(this.items[i]);
  }
}

@Component({
  selector: 'app',
  directives: [SubComponent],
  template: `
    <div>
      <sub-component [items]="items" (onItemSelected)="itemSelected($event)">
      </sub-component>
    </div>
  `
})

class App {
  items: string[];

  constructor() {
    this.items = ['item1', 'item2', 'item3'];
  }

  itemSelected(item: string): void {
    console.log('Selected item:', item);
  }
}

bootstrap(App);

Solution 4:

In parent Component template:

<hero-child [hero]="hero">
</hero-child>

In child Component:

@Input() hero: Hero;

Source: https://angular.io/docs/ts/latest/cookbook/component-communication.html

Solution 5:

There are many ways. This one is an example using propagation between parent and child elements. This is very efficient.

I submitted an example that permits to view the usage of two ways databinding within two forms. If somebody can provide a plunkr sample this would be very nice ;-)

You may look for another way using a service provider. You may have a look at this video too for reference: (Sharing Data between Components in Angular)

mymodel.ts (data to share)

// Some data we want to share against multiple components ...
export class mymodel {
    public data1: number;
    public data2: number;
    constructor(
    ) {
        this.data1 = 8;
        this.data2 = 45;
    }
}

Remember: There must be a parent that will share "mymodel" to child components.

Parent component

import { Component, OnInit } from '@angular/core';
import { mymodel } from './mymodel';
@Component({
    selector: 'app-view',
    template: '<!-- [model]="model" indicates you share model to the child component -->
        <app-mychild [model]="model" >
        </app-mychild>'

        <!-- I add another form component in my view,
         you will see two ways databinding is working :-) -->
        <app-mychild [model]="model" >
        </app-mychild>',
})

export class MainComponent implements OnInit {
    public model: mymodel;
    constructor() {
        this.model = new mymodel();
    }
    ngOnInit() {
    }
}

Child component, mychild.component.ts

import { Component, OnInit,Input } from '@angular/core';
import { FormsModule }   from '@angular/forms'; // <-- NgModel lives here
import { mymodel } from './mymodel';

@Component({
    selector: 'app-mychild',
    template: '
        <form #myForm="ngForm">
            <label>data1</label>
            <input type="number"  class="form-control" required id="data1 [(ngModel)]="model.data1" name="data1">
            <label>val {{model.data1}}</label>

            label>data2</label>
            <input  id="data2"  class="form-control" required [(ngModel)]="model.data2" name="data2" #data2="ngModel">
            <div [hidden]="data2.valid || data2.pristine"
                class="alert alert-danger">
                data2 is required
            </div>

            <label>val2 {{model.data2}}</label>
        </form>
    ',
})

export class MychildComponent implements OnInit {
    @Input() model: mymodel ;  // Here keywork @Input() is very important it indicates that model is an input for child component
    constructor() {
    }
    ngOnInit() {
    }
}

Note: In some rare cases, you may have error when the HTML code is parsed, because the model is not "ready" to use at the initialisation of the page. In this case, prefix the HTML code with an ngIf condition:

<div *ngIf="model"> {{model.data1}} </div>