Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to share data/change between components

Tags:

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.

like image 493
Marin Petkov Avatar asked Mar 08 '16 21:03

Marin Petkov


People also ask

How many ways we can share data between components?

There are five ways to share data between components: Parent to child component. Child to parent component. Sharing data between sibling components.

How do you pass data between two child components?

Sharing data between sibling components: Sharing data between siblings can be done by using points 1 and 2. First share data between the child to parent using output decorator and EventEmitter. Once received data in parent component share it with another child component using Input decorator.


2 Answers

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:

  • Delegation: EventEmitter or Observable in Angular
  • Delegation: EventEmitter or Observable in Angular
  • Is possible to have two template for single call service in AngularJS 2
like image 159
Thierry Templier Avatar answered Oct 04 '22 05:10

Thierry Templier


In your use case I would use services. Services are used to communicate its data to other components. One component could update this data to the service, and another component could read from it. Or both components could just read from it, and the server itself gets it's data from the 'outside world'.

You use input to pass along data from the parent to the child and you use output to output events from the child to the parent.

You use ngOnChanges to do something when something changes in the component itself, but I prefer to use get() and set() functions for that.

At least, that is my take on it :)

like image 37
Poul Kruijt Avatar answered Oct 04 '22 06:10

Poul Kruijt