October 2017

Volume 32 Number 10

[The Working Programmer]

How To Be Mean: Angular Plays Fetch

By Ted Neward | October 2017

Ted NewardWelcome back again, MEANers.

In my last column, I wrote, “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.” I then proceeded to go on for another couple thousand words without ever actually doing anything across HTTP.

To be fair, the intention of that column was to prepare the code for being able to fetch data from an HTTP API by placing the HTTP request code inside of an Angular service. Given that I hadn’t looked at an Angular service prior to that point, I first needed to work through the basics of what an Angular service looks like and how to use it from the various components that might need it. Alas, all that work left me with no room to get to the actual goal: the data residing in the HTTP API endpoints.

It’s time to fix that. Let’s go fetch some data.

Speaker API

First things first, I need an HTTP API that I can hit with requests. Somewhere back in that pile of old MSDN Magazine issues, there’s the last version of the server-side API that I built a year ago. If you actually dig it up, you’ll find it’s an API that provides “Persons,” not speakers. Amazing how a project’s goals can “drift” over time, isn’t it?

Given how simple that MEAN server was, and the fact that the only differences between then and now would be the objects returned, I could easily change the code to push Speaker objects (defined in the last column) back and forth. But doing that means missing out on a golden opportunity to point out something else: the Node.js community has a plethora of “MEAN stack frameworks,” including Sails.js (sailsjs.com) and one I’ve been working with called Loopback (strongloop.com), among others. Many of these frameworks are “built up” off the same tools I used a year ago (Express, MongoDB and the like) to construct a server-side HTTP API, while gluing things together a bit more tightly and painting a lovely veneer over the top. Or you could put together an ASP.NET MVC Web API endpoint. The server-side implementation doesn’t matter, as long as it hides behind HTTP.

The first order of business is to see what the interface for the API looks like—what URLs to use, what verbs to use with those URLs, the expected parameters and the format of the returned JSON response. Given that this is a pretty simple service, it’s easy to imagine what they will look like, so there’s no need to list them out formally here. (Just enter GET on /speakers for the full list of speakers, GET on /speakers/:id to retrieve a particular speaker and so on.)

SpeakerService

If you recall from my last column, the module responsible for fetching data was called SpeakerService. Given that the entire point of the service is to encapsulate away the details of fetching and storing Speakers, you can assume that most (if not all) of the work I’m about to show here will be contained there.

When I left things last time, SpeakerService drew results from an in-memory constant:

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

The service will eventually need to provide update and deletion facilities, but for now let’s just work with the read operations. Notice how in each case, the “get” method returns an immediate object—either the Speaker itself, or an array of Speakers. For an in-memory operation, this is pretty reasonable, but for an operation that will take place through the TCP/IP stack, it’s not. Network operations take time, and forcing the caller to wait until the network operation finishes—assuming it ever does—puts an undue burden on the developer building the front-end, because now the front-end has to somehow keep the UI responsive while blocked waiting for the response. Historically, in other UI platforms, the solution has been to have the UI incorporate multiple threads, forcing the UI developer to account for the discrepancy. In Node.js, however, the preferred approach is to hand back not an actual object, but the promise that one will show up—eventually. These are called, not surprisingly, Promises.

The short concept of a Promise is that it’s a placeholder, an object that will, at some point in the future, hold the desired result. But until that happens, execution can continue forward without the actual result. Fundamentally, a Promise is an object wrapped around a callback that will yield the value desired, called a “resolver.” (Actually, a Promise is wrapped around two such callbacks, the other being called the “rejecter,” in case the Promise can’t fulfill its obligations.)

Because HTTP traffic is by definition slow, it behooves me to model the service as such, at least until I put in the network traffic. Combining the switch to Promises with a short two-second delay in returning the data suffices nicely. Figure 1 shows the modified code.

Figure 1 Modeling the Service with the Promise Placeholder

@Injectable()
export class SpeakerService {
  constructor() { }
  getSpeakers(): Promise<Array<Speaker>> {
    return new Promise((resolve, reject) => {
      setTimeout( () => {
        resolve(SPEAKERS);
      }, 2000);
    });
  }
  getSpeaker(id: number): Promise<Speaker> {
    return new Promise((resolve, reject) => {
      setTimeout( () => {
      resolve(SPEAKERS.find(speaker => speaker.id === id));
      }, 2000);
    });
  }
}

This code simulates a network delay of two seconds before serving up data, but developers who want to more closely simulate network conditions could throw a random failure into the mix, choosing to call reject once every 20 calls or so.

Upvotes and Such

On the component side, the templates and logic must be adjusted slightly to accept Promises instead of the real data. That’s actually easier done than said, as shown in Figure 2.

Figure 2 Working with Speaker Values

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

  constructor(private speakerSvc: SpeakerService) {
    this.speakerSvc.getSpeaker(1).then( (data) => {
      this.speaker = data;
    });
  }
}

Two major changes happened here: First, the speaker field was initialized to an empty Speaker value, so that when Angular first evaluates the component’s template, there’s always a Speaker object with which to work. Second, the constructor is modified to make use of the returned Promise by providing a then method call, which will take the data handed by the Promise and make use of it. In this case that means saving the constructor into the component’s speaker field. The template remains unchanged, and if “ng serve” is still running in the background, reloading the page yields something curious. The page comes up, but the “votes” part of the Upvote component remains empty … for two seconds. Then the fetched value appears, entirely as if by magic.

This is the beauty of a Promise—Angular understands that when the value changes, it needs to re-render that part of the DOM that has changed, and does so automatically. Sweet.

Speaker, O Speaker, Where Art Thou, Speaker?

But I’m still fetching data locally. How boring. Time to go back to the SpeakerService and start doing calls over HTTP. This means making a few changes: importing the HttpModule (the Angular team chose to break it out of “core”), referencing the HttpService, and making use of it in the SpeakerService. Note that (not surprisingly) the HttpService makes use of Promises, so the subtle shift in the SpeakerService API makes using the HttpService pretty straightforward, as Figure 3 illustrates.

Figure 3 Using the HttpService

@Injectable()
export class SpeakerService {
  private speakersUrl = "http://localhost:3000/api/Speakers";

  constructor(private http : Http) { }
  getSpeaker(id: number): Promise<Speaker> {
    return this.http.get(`${this.speakersUrl}/${id}`)
               .toPromise()
               .then(response => {
                  let raw: any = response.json();
                  let speaker = new Speaker();
                  speaker.firstName = raw.FirstName;
                  speaker.lastName = raw.LastName;
                  speaker.votes = new Upvote(raw.Votes);
                  speaker.id = raw.id;
                  return speaker;
               });
  }

The reason for the call to toPromise will become more clear in a future piece, when I discuss Observables. For now, this call is necessary to convert the returned object for use as a Promise. Note that to make the code in Figure 3 work, one more import is necessary. To bring in the to Promise method, make sure to add the following code to the top of the file:

import 'rxjs/add/operator/toPromise';

It may seem odd that the SpeakerService is "transforming" the returned JSON from the API into a local Speaker object, but this is actually a fairly common task. The JSON published by an API is often not quite the right "shape" for use by the UI, and so it needs to be massaged into the local concept of what that type should look like. In this particular case, I'm assuming that the service is returning the speakers with uppercase field names (less unusual than you might think), and taking the additional step to transform the "Votes" field into an Upvote object. While some "full stack" systems will try to insist there's one schema definition that can be shared across both client and server, it's generally safer to assume that there isn't, and perform "adjustments" to the data received to transform it into something the UI finds more palatable.

In some cases, that "adjustment" consists of leaving some amount of data on the floor, or "flattening" it for easier use, or some other transformation. For example, if the server sends back distinct "Speaker" and "Talk" objects, the Speaker-Service may wish to "flatten" that into the single Speaker object that I'm using, collecting all of the votes for each talk in the speaker's repertoire into a single scalar value.

Go ahead and save. Assuming you have a server running on localhost at port 3000, you should see everything work as it did before the flip to using HTTP. (If you don't have a server running on localhost, feel free to change the URL in the SpeakerService to http://msdn-mean.azurewebsites.net/, which is where I've parked the newly refurbished Speaker API. Feel free to browse the complete set of API endpoints using the "/explorer" URL there, if you wish -- that is a Swagger-generated UI, which comes out of the box when working with Loopback.)

Wrapping Up

The HttpModule (and its corresponding service object, HTTP) certainly has support for PUTs and POSTs and all the other goodness that HTTP offers, but those will be addressed once I've added the ability to edit and add new Speakers in the Angular app. For that matter, I still need to flesh out this front end a bit more, by showing a list of all the speakers and then allowing the user to drill down into a specific one -- the classic "master-detail" approach that so many applications use (and abuse). To do that, I'll need to talk about how Angular manages "routing" (to move between components, as if they were "pages" in the application) and "forms" (for doing data entry). But all that has to wait for next time. Until then ... 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.

Thanks to the following technical expert who reviewed this article: James Bender


Discuss this article in the MSDN Magazine forum