The Official Ionic Blog

Build amazing native and progressive web apps with HTML5

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 [email protected]/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 [email protected]/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.

  • Alexandru Pufan

    I always wondered what’s the best approach when we have multiple types of data. For example, let’s say we have ice-cream, cakes and burgers, each one having different functions related to it. Should we make a separate service for every one of them, or a big service to store them, and separate services to manipulate them?

    • Alex Muramoto

      There’s different ways you can go, depending on how much they differ and how you want to structure the services for your app. If there’s a lot of shared logic, like persisting to a DB, validating user input, etc, then those could be good candidates to each be their own service so they can be used as utilities. That way instead of like `saveCake()`, `savePie()`, etc, you’d just have `save(foodType: string)`, or something like that.

      As far as the actual objects (cake, burger, etc), I personally like to keep services somewhat lightweight so that it’s easier to reason about what any given service is doing, but this is really going to depend on how different each object is. If there’s a lot of shared functionality, a single larger service might make sense. If they’re all very different, then separating them might make sense.

  • Poul

    Nice article it’s pretty well explained. Adding up… I wouldn’t set a service to hold state as it might give a misleading tip on how services should be consumed. As it is an injectable member, can be consumed all around the app. So other component requesting the instance might get unexpected results (consumed icecream) from their own point of view. Many services could consume a “ApiService” that caches API requests that are executed with high frequency, but not holding the state in the Service “Itself”

  • pskhodad

    One more approach based on services would be using ngrx store and effects. I find it very useful, easier to debug and track the state across views. Also performant due to OnPush changedetection.

    One reason I avoid NavParams is for not getting tightly coupled with Ionic routing. It looks more future proof to avoid tying up anything than absolutely necessary to NavController. In future if there is need to use Angular router then approach based on NavParams might have issues.

  • johnskwondoe

    Good article but I think to make it less confusing for newcomers it could be tweaked to reflect how providers are referenced in the RC (and going forward).

  • bandito

    Never thought of doing this with a service. How weird of me :/

  • Randolph Octavio

    Hi Alex, very thanks for this post. I’ve learned a lot.

    I have a question. I use in angular 2 component communication with @[email protected], why ionic2 does not use this approach?

    • Savan

      Still a newbie for both angular 2 and ionic2, but agree with Randolph.

      Can we use @[email protected] instead?
      As a developer i would prefer to use existing angular2 concepts so we can reuse our skills

    • Alex Muramoto

      @[email protected] can definitely be used, though it would be for parent/child component communication. This is great for child components within a page, but different views in an Ionic app are siblings, so it wouldn’t be an ideal solution in that case.

      • Randolph Octavio

        Ok, thanks Alex. I see your point. I’m curious, is that some kind of pattern design? Where can i read about it? Or it’s just simple logic? Why is more convenient to use @[email protected] with parent/child components?

  • pskhodad

    Hi Alex,

    How about deeplinks?
    For deeplinks, there could be need to prefer NavParams over services.

    • Alex Muramoto

      Definitely! There’s a number of options for moving data around an app than what I’ve included here. For example, in the case of deep linking, client-side storage might also be a good option, depending on the scenario.

  • Pie Cy

    i have search a lot of animation information about ionic and most of them is simple animation like animate a list or card or picture. i am curious can hybrid app like ionic capable to do animation like the link given.

    https://dribbble.com/shots/2970544-Craigslist-on-mobile

    you can see that when the category is click it animate and expand to push a new page, what default in ionic 2 is only slide in or fade in when we do this.nav.push(NewPage)

  • Couim

    I’m looking for something similar of Restful client tutorial but for SOAP. I know that a tutorial already exists for ionic 1 but not for ionic 2. Any suggestions ? 🙂 If someone is willing to do that I’m interested

    • Alex Muramoto

      Do you mean you are looking for a tutorial on how to consume a SOAP API in your app?

      • Couim

        Yeah exact 🙂 With Ionic 2, by using wsdl etc… 🙂

        • Alex Muramoto

          Any Angular2 lib for doing The SOAP should work =)

          I haven’t used this personally, but it was the first thing on GitHub that came up. If you try it and it works, let me know!

          https://github.com/autopulous/angular2-soap

          • Couim

            Yeah I’ve seen that but it’s necessary to enter all XML envelope so if we have an important web service, it’s not very modular I guess. So i’m looking for an alternative in which we not have tu put all XML envelope for each server methods 🙂

  • http://angular-meteor.com/ Urigo

    This is a great article for a much needed subject!
    One thing I don’t like about the regular Angular services approach is that it makes the Service tied into all the Components that are using it.
    For example – if I fetch data from a server, according to this I approach I would do that fetching logic in a service, because I don’t want to repeat this logic in other Components (DRY).
    But that also means that if I need to change something in the logic of the service, I need to check all the Components associated with it, and if I have a large app, that has great potential for bugs.
    I think that in the React world which embraced Components early on, came to be other best practices that fit the Component based apps better – leaving the logic of fetching in a service, but the data/endpoint specification stays on the Component so the Component would stay self-contained, even if it needs to fetch data from a server or a central store.
    Sorry for the long message, I also wrote a blog post about it and I guess I’m just happy to see more discussions about the best way to handle data.
    My post (not as a plug, I promise) – https://medium.com/apollo-stack/data-management-and-ajax-server-fetching-for-angular-components-70aedb98244b#.prrlbthq6

  • Young Park

    After using angular2 for a while, it’s always better to use Services than navParams. data scales, and once it scales, services work better.

    agreed that navParams to use for deeplinks