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'); | |
| } | |
| } |
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.
Comments are closed.
1 comment
fantastic, many thanks <3