We need to make our collection of Albums accessible throughout our application. That way, the Album object properties are available to view in its detail page. To do this we need to add a service. In Angular, services rely on the concept of dependency injection we just covered.

In this lesson, we'll walk through creating a service, injecting it throughout our program, and retrieving information from a service in our components.

Creating a Service

Let's jump right in and craft our new service! We'll discuss the details and specifics of services, and Angular-specific dependency injection along the way.

Separate Data

Our list of Albums currently resides in the MarketplaceComponent. Let's separate this into its own file for the service to manage instead. Create a new file in src/app folder called mock-albums.ts.

We'll import our Album model at the top of this file:

src/app/mock-albums.ts

import { Album } from './album.model';

And move our array of Albums from the MarketplaceComponent to our new file:

src/app/mock-albums.ts

import { Album } from './album.model';
export const ALBUMS: Album[] = [
new Album("Pulse", "Pink Floyd", "A live album by the English progressive rock band originally released in 1995, on the label EMI in the United Kingdom.", 1),
new Album("Funhouse", "The Stooges", "The second album from the American rock band, released in 1970 by Elektra Records.", 2),
new Album("Twilight of the Thunder God", "Amon Amarth", "Seventh album by the Swedish band, released in 2008, based on Thor's battle with the serpent Jörmungandr.", 3),
new Album("Dilate", "Ani DiFranco", "Her highest-selling and most acclaimed album, released in 1996.", 4),
new Album("Chopin - Complete Nocturnes", "Brigitte Engerer", "Released in 2010, this is Engerer's own rendition of the classical composer Chopin.", 5),
new Album("Axis Bold As Love", "The Jimi Hendrix Experience", "Second studio album by the English-American band, released in 1967.", 6)
];

Notice we are using the const keyword to make this array a constant. We are naming the array of Albums in all capitals because this is standard naming convention for constants.

Notice this is structured similarly to a component file. It begins with imports statements, ends with an exported class, and includes a decorator. However, you'll notice the decorator reads @Injectable, not@Service. Using this decorator makes this class available to our injector. Remember, the injector is responsible for delivering the service where it needs to be.

Next, let's import our Albums at the top of the file. This will allow our service access to both our Album model, and the actual list of Album objects from our mock-albums.ts file:

As you can see, it simply returns the list of Albums we imported into the file in the line import { ALBUMS } from './mock-albums';

Injecting a Service into a Component

Our MarketplaceComponent file needs access to these Albums, since we removed the array from its class a moment ago. Because the service is now responsible for managing this information, we'll inject our service here.

Import the Service

First we'll import the new service into the MarketplaceComponent's file:

src/app/marketplace/marketplace.component.ts

import { AlbumService } from '../album.service';
...

Add Service to Constructor

Then we'll add the service as a parameter in its constructor. The MarketplaceComponent should already have a constructor() method with one parameter, a Router object. We'll add AlbumService as a second parameter:

Just like the Router already present, this ensures all new instances of MarketplaceComponentalso have an instance of AlbumService, accessible by calling this.albumService anywhere in the MarketplaceComponent class.

Registering Providers

Next we'll need to add a new property to the component's decorator called providers. It will correspond with an array containing our AlbumService:

As discussed, Angular's dependency injection system has three primary parts: Services, Injectors and Providers. The providers array above tells Angular to create a new instance of AlbumService when it creates a new MarketplaceComponent.

Listing a provider in a component is known as registering a provider. The providers array tells Angular to create a fresh instance of AlbumService when it creates a new MarketplaceComponent. The component may then use this service to access the data it controls, like our list of Albums.

Automatically Retrieving Data from a Service

So far we have:

A source of data. This could be an API, or a database in other applications, but for us it's simply a mock-albums.ts file.

A service that imports that data, and contains a getAlbums() method to return it.

A component that has both registered the service as a provider, and has an instance available to it (because it was included in the constructor())

So, we have a path in place for data to travel into the MarketplaceComponent, but we still need to call the service's getAlbums() method somewhere to retrieve that data.

Where do we call it? Well, we don't want the user to have to click a button, or submit a form to receive the list of Albums in the MarketplaceComponent. They should just be there already when it loads!

Thankfully, there's a place we can call the getAlbums() method automatically: ngOnInit()!

We previously removed the interface and lifecycle hook method from this component, but that's alright! We'll add it right back in. First, we'll import the interface:

And, if we build and serve our application, we should see the Marketplace page still lists all our Albums, just like before, even though this data is now being gathered from our new service. Nice work!

Injecting Services in Multiple Components

However, we also need to use our service in the AlbumDetailComponent. That way, when the user clicks an individual Album in the MarketplaceComponent they may navigate to a page displaying all the details for that particular Album.

We can currently display the Albumid from the URL in the AlbumDetailComponent. However, now that we have a service, we can inject the service into the AlbumDetailComponent, and retrieve the entireAlbum object that matches the id from the URL.

To do this, we'll follow the same steps to inject a service as we did above:

(When constructor() parameter lists get long, you can enter line breaks between the items for easier readability, if you'd like).

Now we can gather the specific Album object the user has requested to view from the service in the component's existing ngOnInit() method. However, right now our service only contains a method to return allAlbums. Let's define another method that can return a specificAlbum:

Here, we've defined a getAlbumById() method that takes the id of the Album we're seeking as a parameter. It simply loops through all Albums in our ALBUMS constant, and returns the one with the id we're looking for.

Now, let's define a property in AlbumDetailComponent to contain the Album object whose details we'd like to display:

This method passes our albumId property into our new getAlbumById method in our service, which returns the album we're interested in so that we can store it in the Album Details Component property albumToDisplay.

Next, let's update the AlbumDetailComponent's template to display details about the selected Album: