Angular 2 and Ionic 2 Data Services
Part 1: Promises and Observables

In this post I continue to improve the IonicRealty sample application I shared last week. In particular, I revisit the data access part of the application. The initial version of the application used simple mock services that weren’t really built the “Angular way”. In this article, I share two approaches to build Angular-style mock services. In part 2, I’ll share a REST implementation of the same services.

Mock services are used for prototypes, during early or offline development, or to test components at a granular level. A key design principle is to make your mock services expose the exact same API as the services they substitute. For example, if you are building a service that stands in for a REST service, it should expose the same asynchronous API as that REST service. API consistency makes the services interchangeable: you can easily switch back and forth between the mock service and the REST service without having to change the calls to the service throughout the application.

Using Promises

Promises are the de-facto standard for asynchronous processing in JavaScript. Let’s take a look at the Property service of the IonicRealty app implemented with Promises (showing only one “find” method for brevity):

property-service.js:

import {Injectable} from 'angular2/core';
import {PROPERTIES} from './mock-properties';

@Injectable()
export class PropertyService {

    findAll() {
        return new Promise((resolve, reject) => resolve(PROPERTIES));
    }

}

To mimic a traditional REST service method, findAll() creates an ES2015 Promise that it resolves immediately. Pages and components can then call findAll() and handle the result in the promise’s then() function as shown here in the ngOnInit function of the PropertyListPage:

property-list.js:

import {OnInit} from 'angular2/core';
import {Page, NavController, NavParams} from 'ionic/ionic';
import {PropertyService} from '../../services/property-service';

@Page({
    templateUrl: 'build/pages/property-list/property-list.html'
})
export class PropertyListPage {

    constructor(nav:NavController, navParams:NavParams, propertyService:PropertyService) {
        this.nav = nav;
        this.propertyService = propertyService;
        this.selectedItem = navParams.get('item');
    }

    ngOnInit() {
        this.propertyService.findAll().then(data => this.properties = data);
    }

}

Using Observables

The problem with the previous implementation is that the Angular 2 http object methods (get, post, put, etc.) don’t return Promises: they return Observables from the RxJS library. If the mock service methods return Promises and the REST service methods return Observables, the two services won’t be interchangeable. There are two options to address this issue:

  1. Standardize on a Promise-based API: In the REST service implementation, convert the Observables returned by the http methods to Promises. This can be done using the Observable.toPromise() function.
  2. Standardize on an Observable-based API: In the Mock service implementation, return dummy Observables instead of dummy Promises.

As an example of the second approach, here is a modified version of the Mock Property service using an Observable-based API:

property-service.js:

import {Injectable} from 'angular2/core';
import {PROPERTIES} from './mock-properties';
import {Observable} from 'rxjs/Observable';

@Injectable()
export class PropertyService {

    findAll() {

        return Observable.create(observer => {
            observer.next(PROPERTIES);
            observer.complete();
        });

    }

}

Pages can then call findAll() and subscribe to process the result:

property-list.js:

import {OnInit} from 'angular2/core';
import {Page, NavController, NavParams} from 'ionic/ionic';
import {PropertyService} from '../../services/property-service';

@Page({
    templateUrl: 'build/pages/property-list/property-list.html'
})
export class PropertyListPage {

    constructor(nav:NavController, navParams:NavParams, propertyService:PropertyService) {
        this.nav = nav;
        this.propertyService = propertyService;
        this.selectedItem = navParams.get('item');
    }

    ngOnInit() {
        this.propertyService.findAll().subscribe(data => this.properties = data);
    }

}

Providers

For Angular to be able to inject PropertyService in PropertyListPage, you need to add it to the list of providers. You could do it at the page level:

@Page({
    templateUrl: 'build/pages/property-list/property-list.html',
    providers: [PropertyService]
})

But since the PropertyService is used in many pages throughout the application, I declared it at the App level (in app.js):

import {PropertyService} from './services/property-service';

@App({
    templateUrl: 'build/app.html',
    config: {},
    providers: [PropertyService]
})

Source Code

The modified source code and installation instructions for the application are available in this repository on GitHub.

Feedback

Since Angular 2 is still in beta and a lot of this is new to Angular 2, I’m interested in your feedback. How do you build your own mock and actual services? What strategy have you developed to make them easily interchangeable?

css.php