A common design pattern you might come across in Ionic is wanting to perform some kind of UI-based action from within a service. For example, if an API request returns a 403 / not authorised header, you’ll want to display an alert and redirect to a login page. It’s not DRY to repeat the alert / redirect code on every single component that calls the API, so you might feel like doing this from within the service itself. This is Bad Practice™. Services should only deal with data and shouldn’t interact with the view.

So what can we do? Luckily there’s an easy way to solve this problem using observables. Here’s a simple example of an ApiService with an observable for errors:

import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
@Injectable()
export class ApiService {
private errorObserver: any;
public error: any;
constructor(public http: Http) {
this.errorObserver = null;
this.error = Observable.create(observer => {
this.errorObserver = observer;
});
}
get(url) {
let headers = new Headers({
'X-API-KEY' : 'my-api-key',
'Content-Type': 'application/json; charset=utf-8'
});
let options = new RequestOptions({ headers: headers });
return this.http.get(url, options)
.map(res => res.json())
.catch(error => this.handleError(error));
}
private handleError(error) {
this.errorObserver.next(error);
return Observable.throw(error.json().error || 'Server error');
}
}
view raw api-service.ts hosted with ❤ by GitHub

Then, in app.component.ts, we subscribe to the error observable. From here we can use the AlertController and NavController to perform the relevant actions, but for the sake of simplicity I’ve just added some comments.

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { ApiService } from '../providers/api-service';
@Component({
template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
...
constructor(platform: Platform, private apiService: ApiService) {
platform.ready().then(() => {
...
this.apiService.error.subscribe((error) => {
if (error.status == 403) {
// unauthorised, redirect to login
}
// error, alert message
});
});
}
}

I’d like to thank sebaferreras from Stack Overflow for his answer that helped me develop my solution.