September 2017

Volume 32 Number 9

[The Working Programmer]

How To Be MEAN: Servicing Angular

By Ted Neward | September 2017

Ted Neward

Welcome back again, MEANers.

One of the common needs of an Angular application is to obtain or update data from locations not inside the browser—which is usually everywhere. Most often, an Angular app will need to exchange data (usually in the form of JSON packets) with an HTTP-based API implementation somewhere on the Internet.

Unfortunately, this doesn’t really “fit” well with the notion of a component. Multiple components will often need to communicate with the same server through the same API implementation, and to duplicate that code in each and every component would get ugly. Certainly, as you’ve seen with the Upvote component (msdn.com/magazine/mt784667), it’s possible to create a standalone TypeScript class and simply make use of that—essentially bringing the concept of “library module” into Angular. But as so frequently happens, the Angular team anticipated this need, and has given unto us … services.

A service, in Angular terms, is an injectable module: “module” because you’ll create a TypeScript module that encapsulates the service’s implementation, and “injectable” because through the use of some decorators, you’ll make the service known to Angular, so that those components that wish to use it simply have to declare it as a parameter (similar to how the @Input/@Output decorators worked earlier) and lo, they will receive one.

(Take note: An Angular service is not implicitly in a 1-to-1 relationship with an “API service” or “microservice” or any of the other overloaded uses of the term. If the term is still confusing, remember that an Angular service is simply a TypeScript module whose users are always other Angular components—humans never interact with an Angular service directly.)

Let’s have a look.

SpeakerService

The start of every service will typically begin with asking the Angular CLI to create the basic scaffolding for you, so let’s start with that: From a command prompt, type “ng generate service Speaker.” This will generate two files—speaker.service.ts and speaker.service.spec.ts—and inside of them will be the basic outline for the SpeakerService class (the “Service” is an assumed-desired suffix to the name specified on the command-line) and the testing code, respectively.

However, as of this moment, you don’t have a Speaker type to be returned from the service! (As legendary cartoon character Homer Simpson would say … “D’oh!”) Again, this is easily fixed by asking the CLI to create a Speaker type to use: “ng generate class Speaker.” This time, the CLI will generate a single file, speaker.ts, which contains only the class Speaker. This is important to notice: The CLI is looking to enforce Angular conventions by automatically putting the appropriate suffix on the various types being generated. There’s a command-line parameter to control the naming, but, frankly, just go with the defaults here—it will make many things much easier over time.

Speaker

I’m going to keep the Speaker type pretty straightforward for now. Obviously, this could get as large and as complicated as necessary, but it wouldn’t teach you anything about Angular to do it that way. The Speakers have some simple properties, and you’re going to take the simple (perhaps naïve) perspective that an evaluation is of the speaker, and not of the talk. Were this a production application, you’d probably have a “Speakers HAVE-MANY Talks” relationship and then “Talks HAVE-MANY Upvotes” relationship, but, again, that doesn’t really show off Angular. So Speaker looks like this:

import { Upvote } from './upvote';

export class Speaker {
id: number;
firstName: string;
lastName: string;
votes: Upvote;
}

It’s a simple class, enough so that I’m going to skip showing the tests for it. (Note that if you go looking for the speaker.spec.ts file, it doesn’t exist—by default, the CLI doesn’t generate a test file for a simple class like this. If you want a test file—and you really, really should want one—then you should pass “—spec true” on the command line when doing the ng generate command.)

SpeakerService

Now that you have a type to represent speakers, you can build out a service that will basically act as a repository (meaning, following the basic concept of the Repository pattern) for speaker instances. For the moment, this will live in memory, and its (so far, read-only) implementation should be fairly clear, as you can see in Figure 1.

Figure 1 Implementing the SpeakerService

import { Injectable } from '@angular/core';

import { Speaker } from './speaker';
import { Upvote } from './upvote';

@Injectable()
export class SpeakerService {
constructor() { }
getSpeakers() : Array<Speaker> {
return SPEAKERS;
}
getSpeaker(id: number) : Speaker {
return SPEAKERS.find(speaker => speaker.id === id);
}
}

The SpeakerService looks like pretty much any other TypeScript class, with the sole exception of the @Injectable decorator on it—this is what will tell Angular that the SpeakerService can be dependency injected into those components that want to use it. The rest of the implementation is pretty straightforward. Right now, everything is working off of a constant array of Speaker instances held as a “private global” inside the speaker.service.ts file, called “SPEAKERS,” shown in Figure 2.

Figure 2 The Constant Array of Speaker Instances

const SPEAKERS : Array<Speaker> = [
{
id: 0,
firstName: "Ted",
lastName: "Neward",
votes: new Upvote(30)
},
{
id: 1,
firstName: "Rachel",
lastName: "Appel",
votes: new Upvote(35)
},
{
id: 2,
firstName: "Nick",
lastName: "Landry",
votes: new Upvote(3)
},
];

In effect, this array of three elements is your database, at least for the moment. When I talk about how to use Angular to send and receive HTTP requests, this array will be populated from JSON returned to use from the server’s API implementation, and any changes you make will be to the array first. But for now, just hold it as a constant array.

Notice that the Upvote component is used directly inside the Speaker instances—this will allow the UpvoteComponent to build around the Upvote instances, and essentially preserve the whole MVC approach even at a fractal level inside models. Spiffy.

Using SpeakerService

The UI currently isn’t much to speak of (pun intended), but let’s look at how to make use of the service anyway. Assume that the homepage simply wants to grab the first Speaker out of the Speaker­Service (using the getSpeaker call with a parameter of “0”) and then extract the Upvote out of it for use in the UpvoteComponent on that page. Practically speaking, that means that the app.component file will need to get a reference to a SpeakerService, use that to obtain a Speaker, and then get a reference to the Upvote out of the Speaker and pass that to the UpvoteComponent. The latter part is pretty easy—you just modify the app.component.html template to reference an AppComponent field called speaker, from which you’ll get the Upvote instance, like so:

<h1>
{{title}}
</h1>

<app-upvote votes="{{speaker.votes}}"><b>Current Upvotes:</b></app-upvote>

So far, so good. But this means that you need a local speaker field inside the AppComponent, and that’s where you’ll also use the SpeakerService, as shown in Figure 3.

Figure 3 Using the SpeakerService

import { Component } from '@angular/core';

import { Speaker } from './speaker';
import { SpeakerService } from './speaker.service';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'SpeakerApp';
speaker: Speaker;

constructor(private speakerSvc: SpeakerService)
{
this.speaker = this.speakerSvc.getSpeaker(0);
}
}

Wow, this Angular stuff is actually pretty easy. Save everything, do an “ng serve” and … blank screen. What gives?

Providing SpeakerServices

Opening up the developer tools console in your browser yields a hint: At the top of the stack trace Angular says “No provider for SpeakerService!” in a rather loud voice. This is because Angular needs one more bit of glue in order to do the @Injectable magic. It needs to know that the code in this file is actually something that it’s supposed to know about. By default, Angular doesn’t just assume every .ts file in the directory is part of the application, so you need to set up a “provider” for the SpeakerService. This is done in the AppModule (app.module.ts), and it doesn’t require a rocket scientist to figure out where. There’s an empty “providers” array right in the AppModule’s NgModule decorator, just begging to have Speaker­Service listed there (after doing the appropriate import to reference the speaker.service.ts file, of course). Figure 4 has the goods.

Figure 4 Set Up SpeakerService in the Providers Array

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

import { AppComponent } from './app.component';
import { UpvoteComponent } from './upvote/upvote.component';
import { SpeakerService } from './speaker.service';

@NgModule({
declarations: [
AppComponent,
UpvoteComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [ SpeakerService ],
bootstrap: [AppComponent]
})
export class AppModule { }

Save, run (or just wait for the browser to refresh if you still have “ng serve” running in the command-line window) and look at the loveliness.

Property Bindings

Except … it’s not lovely. Matter of fact, if you’re running the code, you’re probably noticing that instead of a number (30), the page is displaying something like Current Upvotes: [object Object]. What’s up with that?

If you look back at the UpvoteComponent template, you’ll notice that it’s using the {{ }} syntax to obtain a value for the HTML:

<app-upvote model="{{speaker.votes}}"><b>Current Upvotes:</b></app-upvote>

You could get away with it for a while, but in this particular case, because you're now handing in an actual object (in-stead of the earlier numeric constant), that expression is evaluating to an object, and then converted into a string before being passed in as the input to the UpvoteComponent. Bad column author! No pizza!

What the code should've been doing all along is using a property-binding declaration instead of the string interpolation syntax. Fortunately, this is easily fixed:

<app-upvote [model]="speaker.votes"><b>Current Upvotes:</b></app-upvote>

Note the square brackets around the name of the property you want to bind to, and notice how the value of the prop-erty-bind is a TypeScript expression to use. The expression will be evaluated, and the result bound to the property in question—in this case, the “model” property of the UpvoteComponent. (I chose to rename the “votes” property to “model” because that’s an emerging convention I’m using for my Angular code: For a given component, its model should be stored in a property/field called model, so I can keep straight what the principal state is, as opposed to incidental prop-erties that might be used during the component’s lifetime.) Once the binding takes place, instead of interpolating the ob-ject to the ECMAScript default [object Object] and then passing that in, the object reference itself will be sent to the UpvoteComponent, which recognizes it as an Upvote, and everything is back to loveliness again.

Lesson learned!

Wrapping Up

Things are starting to fall into place now—components are slowly taking shape around an MVC concept, and the models are being retrieved by services. You still need to get those services to obtain real data (as opposed to faking it, the way it’s being done now), and you still need some kind of ubiquitous master-detail “drill-down” display where you begin with a list of speakers and select one for more details, not to mention the ability to edit speaker details and add new speakers to the list. But, things are starting to hang together better now.

After the work done here, you can start to see how the component-based approach works collectively to “build up” an application out of mostly self-contained components. The more componentization that can be put into place, the easier it will be to test those components, and the more testing that can be done, the more the application will resist stupid pro-grammer mistakes during its overall development.

There’s still more work to do … for now … happy coding!


Ted Neward is a Seattle-based polytechnology consultant, speaker and mentor, currently working as the director of Developer Relations at Smartsheet.com. He has written a ton of articles, authored and co-authored a dozen books, and works all over the world. Reach him at ted@tedneward.com or read his blog at blogs.tedneward.com.


Discuss this article in the MSDN Magazine forum