Page 20 - MSDN Magazine, October 2017
P. 20

Figure 1 Modeling the Service with the Promise Placeholder
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 compo- nent’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 surpris- ingly) the HttpService makes use of Promises, so the subtle shift intheSpeakerServiceAPImakesusingtheHttpServicepretty straightforward, as Figure 3 illustrates.
The reason for the call to toPromise will become more clear in a future piece, when I discuss Observables. For now, this call is
Figure 2 Working with Speaker Values
@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);
}); }
}
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 aroundacallbackthatwillyieldthevaluedesired,calleda“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.)
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.
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.
This code simulates a network delay of two seconds before serv- ing 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 3 Using the HttpService
@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; });
} }
@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;
}); }
16 msdn magazine
The Working Programmer


































































































   18   19   20   21   22