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.
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.
There are five ways to share data between components: Parent to child component. Child to parent component. Sharing data between sibling 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.
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:
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With