Bad things can happen when working with remote services. Today we explore error handling in an Observables context (in both the service tier and user facing components).

And we revisit our “Loading…” state to tidy things up on initial page load.

You can always grab tagged source code on the GitHub repo which will be tagged day1, day2, etc.

You can Subscribe to my Fresh Bytecode channel for regular Java/Web-related screencasts, or if you’re just into Angular, checkout the YouTube Playlist where I update all the episodes in this series as they come out.

Here’s the script so you can follow along at home:

[ ] First, fix our import

import { Observable } from 'rxjs/Rx';

[ ] Implement our getCurrentFeed() method to catch errors

return this.http.get('/api/tweets').map((resp: Response) => {
  console.log(resp.json());
  var fetchedTweets = [];
  for (let tweet of resp.json().data) {
    fetchedTweets.push(this.getTweetFromJson(tweet));
  }
  return fetchedTweets as Array<Tweet>;
}).catch(this.errorHandler);

[ ] Implement our error handler (service side) for diagnostic/forensics

errorHandler(err) {
    console.log(err);
    return Observable.throw(err);
  }

[ ] Update our feed component to catch the error (second arg to subscribe) - this should be our user facing error!

ngOnInit() {
this.feedService.getCurrentFeed().subscribe( (newTweets) => {
  this.tweets = newTweets;
}, ( error ) => {
  this.errorText = error;
});

[ ] Display the error text in the component (conditionally) after we render the form, perhaps, but before the timeline…

<div *ngIf="errorText" class="ui negative message">
  <i class="close icon"></i>
  <div class="header">
    {{ errorText }}
  </div>
</div>

[ ] Throw an error to test it out!!!

throw "Internal Error";

[ ] There are scenarios when you do want to trip these errors yourself - for example, bad status codes come back. Let’s updated updateTweet() in our feed service

return this.http.put(url, body).map(
      (resp: Response) => {
        console.log(resp);
        if (resp.status == 204) {
          console.log("Success. Yay!");
        } else {
        throw `Error fetching tweet ${tweet.id}. Received status code: ${resp.status}`;
      }
      }).catch(this.errorHandler);

[ ] Finally, let’s handle the “Loading..” screen scenario. In our feed component backing class, we’ll add a loaded property and use it as a toggle. The third argument to a subscribe is an “on complete” or finally block.

loaded = false;

ngOnInit() {
    this.feedService.getCurrentFeed().subscribe( (newTweets) => {
      this.tweets = newTweets;
    }, ( error ) => {
      this.errorText = error;
    }, () => {
      this.loaded = true;
    });
  }

[ ] Update the markup and we’re done

<div *ngIf="loaded">
    // existing markup..
</div>

<div *ngIf='!loaded'>
  <h2>Loading...</h2>
</div>

[ ] Let’s slow down our fake request timing in our app.module.ts

InMemoryWebApiModule.forRoot(MockDatabaseService, { 
    delay: 3000,  rootPath: 'api/'
})

[ ] Robustness award unlocked! (level 1 :-)

[ ] Next up we’ll start looking at Form validation (continuing our robust theme).