Angular – How to share data/change between components

angular

So let's say you have an interface that has a toolbar, sidebar, and a grid. The toolbar has a drop-down that when a user changes, the content in sidebar and grid changes. Back in Angular 1, I would use a Service to have all of my dynamic data. When something changes in the service, all components that use that service will update as well.

Well in Angular 2, it looks like people are using different methods. I wanted to get your input on which is the preferred way.

  • Static Service
  • OnChanges
  • Inputs and Outputs

The remaining question that I have is if it's best practice to create a new service for each data item that is shared between components or can we just have one service that has an object that stores all shared data.


Original Plunker
– Each change will have its own service

app.component.ts

import {Component} from 'angular2/core';import {NavService} from '../services/NavService';@Component({  selector: 'obs-comp',  template: `obs component, item: {{item}}`})export class ObservingComponent {  item: number;  subscription: any;  constructor(private _navService:NavService) {}  ngOnInit() {    this.item = this._navService.navItem();    this.subscription = this._navService.navChange$.subscribe(      item => this.selectedNavItem(item));  }  selectedNavItem(item: number) {    this.item = item;  }  ngOnDestroy() {    this.subscription.unsubscribe();  }}@Component({  selector: 'my-nav',  template:`    <div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>    <div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>  `,})export class Navigation {  item = 1;  constructor(private _navService:NavService) {}  selectedNavItem(item: number) {    console.log('selected nav item ' + item);    this._navService.changeNav(item);  }}@Component({  selector: 'my-app',  template: `{{title}}  <p>  <my-nav></my-nav>  <button (click)="showObsComp = !showObsComp">toggle ObservingComponent</button>  <div *ngIf='showObsComp'>    <obs-comp></obs-comp>  </div>  `,  directives: [Navigation, ObservingComponent]})export class AppComponent {  title = "Angular 2 - event delegation";  showObsComp = true;  constructor() { console.clear(); }}

NavService.ts:

import {Observable} from 'rxjs/Observable';import 'rxjs/add/operator/share';export class NavService {  private _navItem = 0;  navChange$: Observable<number>;  private _observer: Observer;  constructor() {    this.navChange$ = new Observable(observer =>      this._observer = observer).share();    // share() allows multiple subscribers  }  changeNav(number) {    this._navItem = number;    this._observer.next(number);  }  navItem() {    return this._navItem;  }}

index.html

<!DOCTYPE html><html>  <head>    <title>User Input</title>    <link rel="stylesheet" href="styles.css">    <script src="https://code.angularjs.org/2.0.0-beta.11/angular2-polyfills.js"></script>    <script src="https://code.angularjs.org/tools/system.js"></script>    <script src="https://code.angularjs.org/tools/typescript.js"></script>    <script src="https://code.angularjs.org/2.0.0-beta.11/Rx.js"></script>    <script src="https://code.angularjs.org/2.0.0-beta.11/angular2.dev.js"></script>    <script>      System.config({        transpiler: 'typescript',         typescriptOptions: { emitDecoratorMetadata: true },         packages: {          app:      {defaultExtension: 'ts'},          services: {defaultExtension: 'ts'},        }       });      System.import('app/boot')            .then(null, console.error.bind(console));    </script>  </head>  <body>    <my-app>Loading...</my-app>  </body></html>

app/boot.ts

import {bootstrap} from 'angular2/platform/browser';import {AppComponent} from './app.component';import {NavService} from '../services/NavService';bootstrap(AppComponent, [NavService]);


Revised Plunker for example
– Only one service which stores all data in object. A type will be passed to each listener to check if it needs to do anything based on that type.

Best Solution

You could leverage shared service for this. It could contain both data and observables to subscribe on to be notified when data are updated.

  • Service

    export class ListService {  list1Event: EventEmitter<any> = new EventEmitter();  getLists() {    return this.http.get(url).map(res => res.json())      .subscribe(        (data) => {          this.list1Event.emit(data.list1);        }      );  }}
  • Component

    @Component({  selector: 'my-component1',  template: `    <ul>     <li *ngFor="#item of list">{{item.name}}</li>    </ul>  `})export class MyComponent1 {  constructor(private service:ListService) {    this.service.list1Event.subscribe(data => {      this.list = data;    });  }}
  • bootstrap

    bootstrap(AppComponent, [ ListService ]);

See this question for more details: