Build a Simple Angular Service to Share Data
The content of this post is based on Angular version >= 2.x unless explicitly stated otherwise.
I’ve seen some complex code on Stackoverflow and elsewhere recently where the developer was using Subject and Behavior Subject as part of an Observable solution to simply share data between unrelated components with Angular. In some cases, the Observable approach may be necessary. But in many cases, the solution can be much simpler without Observables.
For example, you may want to retrieve some user preferences and retain them for use by any component in the application. Or you may want to store some user-entered values, such as the selected currency, throughout the application.
Just build a simple service to service up that data to any component that injects the service.
The example in this post defines two unrelated components, Component A and Component B, that want to share data. But there could be any number of components that share this data.
First, we create a service that holds the data. Since a service is a singleton, all of the components that inject this service can share the data from this service:
import { Injectable } from '@angular/core'; @Injectable() export class DataService { serviceData: string; }
This particular service simply provides for sharing the service data. This data could be of any type, including an object. And there could be more code here, such as code to retrieve default values for this serviceData from a back-end server.
Each component then injects this service and uses its value:
import {Component} from '@angular/core' import { DataService } from './data.service'; @Component({ template: ` <div> <h2>Data from A: {{ data }} </h2> <input [(ngModel)] = data /> <br><br> <a [routerLink]="['/b']">Go to B</a> </div> `, }) export class A { get data():string { return this.dataService.serviceData; } set data(value: string) { this.dataService.serviceData = value; } constructor(public dataService: DataService) { } }
There are two key bits of code here:
- The dataService is injected into the constructor.
- The property is defined as a getter/setter so that the current value is get/set from the service.
The second component is similar:
import {Component} from '@angular/core' import { DataService } from './data.service'; @Component({ template: ` <div> <h2>Data from B: {{ data }} </h2> <input [(ngModel)] = 'data' /> <br><br> <a [routerLink]="['/a']">Go to A</a> </div> `, }) export class B { get data():string { return this.dataService.serviceData; } set data(value: string) { this.dataService.serviceData = value; } constructor(public dataService: DataService) { } }
And for completeness, here are the app module and app component: (This was written using a Plunker, so the app module and app component are in the same file.)
import {Component, NgModule, VERSION} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import { RouterModule } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { A } from './a.component'; import { B } from './b.component'; import { DataService } from './data.service'; @Component({ selector: 'my-app', template: ` <div> <h2>Hello {{name}}</h2> <router-outlet></router-outlet> </div> `, }) export class App { name:string; constructor() { this.name = `Angular! v${VERSION.full}` } } @NgModule({ imports: [ BrowserModule, FormsModule, RouterModule.forRoot([ { path: '', redirectTo: 'a', pathMatch: 'full'}, { path: 'a', component: A }, { path: 'b', component: B } ]) ], declarations: [ App, A, B ], providers: [ DataService ], bootstrap: [ App ] }) export class AppModule {}
In this particular case, since both components are routed to each other, this data could instead be passed to the components as a required, optional, or query routing parameter. But this solution works in cases where the components want to share data without necessarily passing it as part of routing.
You can find the plunker here: https://plnkr.co/edit/KT4JLmpcwGBM2xdZQeI9?p=preview
For more information on building services, check out my Pluralsight course: Angular 2: Getting Started
Enjoy!
Ogo Okafor — May 5, 2017 @ 7:58 pm
What’s the difference between using this and using session/local storage?
Eugene — May 6, 2017 @ 12:19 pm
The difference is that this has absolutely nothing to do with session/local storage. This post is not about storing the data. It’s about sharing it. How you store it is entirely up to you.
deborahk — May 8, 2017 @ 2:16 pm
What Eugene said. 🙂 The service only holds values for the lifetime of the application. This is useful for holding onto values that are only needed while the application is running, to pass data between components, and to hold data that was retrieved from storage so that it only needs to be retrieved one time.
Edmundo — May 7, 2017 @ 2:36 am
Pretty!!
Easy and simple.
Something’s the wave of the framework returning types hypes you to keep using the same thing around (i.e. observables).
But still, many times that is unnecessary, until we will have work, pipe, rework, and share data.
Otherwise, this is the best way to share data using the Angular capabilities of syncing properties in the components.
I may suggest, could you add discuss or medium (the commenting platforms) to the site? I tend to recollect good posts like this with them.
deborahk — May 8, 2017 @ 2:17 pm
Thank you so much!
And thanks for your suggestion … but this blog site is maintained by the Microsoft MVP team, so I don’t think I have any authority to add platforms/options to the site.