When working with Angular components often we need to share/pass data between them. To that purpose there are various techniques that we can use:

  • Using Subjects or Observables to share data between components
  • Using shared services.

We have already seen an example of how we can use Subjects to send data to a different component, so  in this post we will focus on the usage of shared services.

Quick guide to Angular Services

Angular services are like any other angular class, but they use the @Injectable decorator. Like this:

import { Injectable } from '@angular/core';
@Injectable()
export class ModelService {
}

We use services to organize the business logic into reusable blocks that we can use from separate components. Services have some interesting properties:

  • They allow us to separate from the controller logic regarding HTTP request, storage, fetching data, and general business logic.
  • They can be instantiated only once, allowing them to act as singletons.

This last point – the ability to create a single instance of a service – is what it going to allow us to use them like a singleton to pass data around components.

Passing data between 2 unrelated components

Let’s create a very simple application with two components:

  • The CreateItemComponent component is a simple form that allows us to create items specifying their name, description and price.
  • ViewItemsComponent is a separate component responsible for writing to console the list of items created by createitem

To share the data – the list of items – between the 2 components we will create a ModelService service.

First we will define an interface for out item objects:

export interface IItem {
    name: string;
    description: string;
    price: number;
}

Now let’s create a service to share our data between the different components of our application. ModelService stores the items in an array, and has two public functions – addItem and getItems – that allow the components to store items, and to query the items currently stored.

import { Injectable } from '@angular/core';
import {IItem} from '../entities/iItem';

@Injectable()
export class ModelService {

    private _items:IItem[] = [];

    addItem(item: IItem) {
        this._items.push(item);
    }

    getItems(): IItem[] {
        return this._items;
    }
}

Now let’s move to our create CreateItemComponent component.
The HTML template defines a simple form:

createItems Template

The CreateItemComponent uses the ModelService service to store all the items create by the user when clicking the “Add item” button. In order to do so it injects the service ModelService in the constructor, and uses the service public method addItem:

import { Component } from '@angular/core';
import {ModelService} from '../services/model.service';
import {IItem} from '../entities/iItem';

@Component({
  selector: 'create-item',
  templateUrl: './createitem.component.html'
})
export class CreateItemComponent {

  private _item:IItem = {name:'',description:'',price:0};

  constructor(private _modelService: ModelService){}

 public get Item():IItem{
     return this._item;
 }

 public addItem(){
     const currentItem:IItem = {
         name:this._item.name,
         description:this._item.description,
         price:this._item.price
     };

    this._modelService.addItem(currentItem);
 }

}

Let’s see how ViewItemsComponent can access the data created by CreateItemComponentViewItemsComponent has a plain button that when clicked should display the list of currently stored items to console:

view items template

When the button “print items” is clicked the ViewItemsComponent component query the ModelService service to obtain the list of items:

import { Component } from '@angular/core';
import {ModelService} from '../services/model.service';

@Component({
  selector: 'view-items',
  templateUrl: './viewitems.component.html'
})

export class ViewItemsComponent {

constructor(private _modelService: ModelService){}

 public printItems(){
    console.log('items in warehouse:');
    console.log(this._modelService.getItems());
 }

}

We also must remember to register the components and service in our application module:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import {ViewItemsComponent} from './components/viewitems.component';
import {CreateItemComponent} from './components/createitem.component';

import {ModelService} from './services/model.service';

@NgModule({
  declarations: [
    AppComponent,
    CreateItemComponent,
    ViewItemsComponent
  ],
  imports: [
    BrowserModule,FormsModule
  ],
  providers: [ModelService],
  bootstrap: [AppComponent]
})
export class AppModule { }<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>

This part is important because by registering our ModelService in the application module we are making it possible for all the components in this model to share the same instance of the service: which is the key to be able to use the service to pass data around components.

Remember  that it is also possible to register a service at component level, but in this case for every instance of the component we will get a new instance of the service.

If we now run our application we see this:
share data between components using services

We add items using the form defined in CreateItemComponent and the ViewItemsComponent is responsible of printing the items list in the console.

Services are commonly used in this way when we want to share data between non related widgets: grids, modals, multi-step wizards, etc.