October 5, 2016
  • All
  • angularjs
  • Ionic
  • Ionic 2
  • Tutorials

Working with Data: Services and Nav Params

Alex Muramoto

It’s pretty much unavoidable that we’re going to need to pass data around our app at some point. Why? Because apps are made up of a whole bunch of views and user interactions and…stuff, none of which tend to exist in isolation. Whether we’re talking about data pulled in via a REST API, collected from a user input, generated client-side, or whatever else, it is often the case that we’ll need to use it in more than one place to create the types of rich experiences users have come to expect from mobile. At the same time, resources like data usage and battery life are limited on mobile, so we want to keep things like network requests and computation to a minimum whenever possible. Keep it DRY (don’t repeat yourself), as the old saying goes.

In today’s blog post, we’ll take a look at two ways to do this in an Ionic 2 app: Angular services and Ionic nav params.

Using an Angular Service

Let’s start with the slightly more complicated but more robust option of building an Angular service. Services allow us to create a shared instance of a class that we can inject into any component of our app via the Angular 2 dependency injection system.

Here’s a quick example involving ice cream, since sharing ice cream is awesome.

First, the skeleton of a service. All we need is to import Angular’s Injectable and a basic class declaration:

import { Injectable } from '@angular/core';

@Injectable()
export class IceCreamService {

  constructor() {}

}

If you want to generate this boilerplate automatically, try using the generate command in the Ionic CLI, which will create the above plus a little extra for you in src/providers/icecreamservice.ts:

ionic generate provider icecreamservice

Syntactically, the only thing that makes this different from an Angular component is the use of the @Injectable() rather than the @Component decorator above the export statement. This tells the Angular compiler that this class is available for use in Angular’s dependency injection system.

Next, let’s add some logic to the service:

export class IceCreamService {

  hadIceCream: boolean = false;

  constructor() {}

  getIceCream() {
    this.hadIceCream = true;
    return 'mmmm... ice cream';    
  }

}

Pretty simple. We have a member variable that tells us if the user has had ice cream already, and a getIceCream() function. Next, let’s use it in a component by doing three things:

  • importing the service
  • adding the provider for the service in the component declaration
  • creating an instance in the component’s constructor
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

// import the class from the service
import { IceCreamService } from '../../providers/icecreamservice';

// the class name of the service is the provider
@Component({
  templateUrl: 'build/pages/home/home.html',
  providers: [IceCreamService]
})
export class IceCreamPage {
  // create an instance of the service
  constructor(public iceCreamService: IceCreamService, public navCtrl: NavController) {

  }
}

Now, all we need to do is use the Valuable And Important data the service provides to our component about whether or not the user has already gotten their fair (and very generous, I assure you) allotment of ice cream:

export class IceCreamPage {
  message: string = "Ice cream. It's Good and You Want It.";
  constructor(public iceCreamService: IceCreamService, public navCtrl: NavController) {}

  getIceCream() {
    if (!this.iceCreamService.hadIceCream) {
      this.message = this.iceCreamService.getIceCream();
    } else {
      this.message = 'Stop hogging all the ice cream =(';
    }
  }
}

Notice how the component can access any public member of the service. In this case, we use the hadIceCream variable in the conditional, as well as the getIceCream() function, and since we injected the service at the component level, it will share a single provider instance with all it’s child components. That’s pretty neat, but better yet, let’s change it so any component in our Ionic app will also have full and consistent access to a single instance of the provider.

To do this, we keep the import statement for the IceCreamService, but remove providers: [IceCreamService] from the component definition in our IceCreamPage. Then we import the service and add the provider to the core AppModule of our app in src/app/app.module.ts:

...
import { IceCreamService } from '../providers/icecreamservice';

@NgModule({

...

  providers: [IceCreamService]
})
export class AppModule {}

Using Ionic Nav Params

Our service is neat, but I’ll admit it could be a little bit of overkill for something as simple as ice cream. On to nav params!

Nav params is a feature of navigation in Ionic that allows us to pass a JSON object from one page to the next. Using our ice cream app, let’s imagine for a moment that we are going to navigate to our IceCreamPage from a different view by using:

this.navCtrl.push(IceCreamPage)

All we need to do to use nav params is pass a JSON object as the second argument of push():

this.navCtrl.push(IceCreamPage, {status: true})

To get this in our IceCreamPage, we import Ionic’s NavParams component:

import { NavController, NavParams } from 'ionic-angular';

Then create an instance of it in our constructor and call get(key) to retrieve the value from the nav params object. In this case, we call get('status'):

constructor(public navCtrl: NavController, public navParams: NavParams) { 
  if (!this.navParams.get('status')) {
    this.message = 'Have some ice cream =)';
  } else {
    this.message = 'Stop hogging all the ice cream =(';
  }
}

Decisions, Decisions

Mobile app development, like life, is full of choices. Services and nav params are both good ways of moving data around our app, but how do we pick? In a real-world scenario, you are almost definitely going to use both options in your app, but on a case-by-case basis in your application logic, the first thing to think about might be where the data is going. If we are navigating from one view to the next with either Ionic’s NavController.push() or NavController.pop(), then nav params could be a good choice. It’s lightweight and easy to implement. Conceptually, it also makes it very easy to reason about how data is flowing between views in our app, since we are passing it off directly.

The next thing to consider is persistence. Will the data be needed in the future, or does it need to be used in more than one view of our app? If this is the case, a service is probably more appropriate. Since services create shared instances that can be injected into any component, they can ensure immutability and/or consistency. In short, we can easily ensure that the data will be the same everywhere it is used, since every component will be consuming the same data source.

But let’s get our priorities straight. The important thing is that the ice cream is safe and accounted for.


Alex Muramoto