The Official Ionic Blog

Build amazing native and progressive web apps with HTML5

Raymond Camden is a Developer Advocate at IBM and frequently writes about Ionic on his blog, where this post originally appeared.

Ionic Native is the spiritual successor to the older ngCordova project. Basically, it provides an Ionic/Angular friendly interface to many common Apache Cordova plugins. To be clear, this isn’t something you have to use in your Ionic application, but it can make using plugins a bit simpler. For today’s demo, I thought I’d work with the Device Motion plugin. This plugin lets monitor the device accelerometer and do…well, whatever based on the motion of the hardware. For my demo (link at the end) I decided on a simple idea–I’d build an app that loads data and then lets you shake the device to update.

I began by building a new Ionic 2 application based on the blank template. For the initial version, I’d build out all the code to load data, display it in a list, and I’d include a button that could be used to refresh the data. (While “shake to update” is cool, you probably want to provide a simple UI element to click as well.)

The first thing I did was create a provider. I made it use hard coded data and set up a simple routine so it could easily add more data to the list. I assume this is self-explanatory, but let me know if you have any questions.

import { Injectable } from [email protected]/core';
import 'rxjs/add/operator/map';

/*
  Generated class for the CatProvider provider.

  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
  for more info on providers and Angular 2 DI.
*/
@Injectable()
export class CatProvider {
  data: any;

  constructor() {
    // hard coded initial data
    this.data = [];

    for(var i=0;i<3;i++) {
      this.data.push(this.makeCat());
    }
  }

  makeCat() {
    return {
      "name":"Cat "+(this.data.length+1),
      "id":+(this.data.length+1)
    }
  }

  load() {
      //add a cat
      this.data.push(this.makeCat());
      return Promise.resolve(this.data);
  }

}

In my view’s logic, I then added in the provider and had it set a local variable, cats, to the result of provider’s load method.

import {Component} from [email protected]/core';
import {NavController} from 'ionic-angular';
import {CatProvider} from '../../providers/cat-provider/cat-provider';

@Component({
  providers: [CatProvider],
  templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

  public cats:Array<Object>;

  constructor(public catProvider:CatProvider, private navController: NavController) {
    this.loadCats();
  }

  loadMore() {
    console.log('load more cats');
    this.loadCats();
  }

  loadCats() {
    this.catProvider.load().then(result => {
      this.cats = result;
    });
  }

}

Again – I’m kinda assuming this is all relatively simple, but just let me know if it doesn’t make sense. Finally, I built out my view.

<ion-header>
  <ion-navbar>
    <ion-title>
      Shake Demo
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content class="home" padding>
  <ion-list inset>
    <ion-item *ngFor="let cat of cats"> {{ cat.name }} </ion-item>
  </ion-list>

  <button danger (click)="loadMore()">Load More</button>

</ion-content>

Here it is running in the browser:

shakeA

Woot. Ok–now for the fun part. First, I have to add in the plugin. The Ionic Native docs remind you of this both on the introductory page of the feature and for each individual plugin. For me, this was done via:

ionic plugin add cordova-plugin-device-motion

Ok, easy enough. Next, I needed to add support to my logic. First, I imported it:

import {DeviceMotion} from 'ionic-native';

Cool. Then I tried the sample code…and it crapped the bed in the browser. Because–of course–this is a device-specific thing. Oops. So the first thing I did was add in support for listening for the platform ready event. Remember – your controller may actually fire before Cordova is ready to let you use hardware features. You can easily listen for this by adding in the Platform object:

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

And then listen for ready:

constructor(public catProvider:CatProvider, private navController: NavController, platform:Platform) {
  this.loadCats();

  platform.ready().then(() => {

That was step one. Step two was to switch to using the incredibly cool Cordova Tools extension for Visual Studio Code. This is an extension created by Microsoft that provides a whole set of awesomeness for folks doing Cordova/Ionic apps with Code. Most recently, they added support for using Ripple within the editor. I haven’t talked about Ripple in a long time, but for folks who don’t remember, it was a browser based testing system for Cordova apps that included some cool extras–like being able to fake GPS and accelerometer data.

So I set up my project for debugging (see the earlier link on Microsoft’s blog for more information) and then fired it off. Now, it is a bit difficult to use this on a laptop, as it is a bit cramped, but I was able to debug my application and ‘shake’ it via Code:

shake1

Nice. So at this point, I needed to do two things–monitor the device motion and then determine when a ‘shake’ happens. The first one is easy:

var subscription = DeviceMotion.watchAcceleration({frequency:200}).subscribe(acc => {

The second one… not so much. Luckily, I’ve done this before in a demo. Basically, I remember the device’s previous values for acceleration, compare it to the current set of values, and if it is “enough”, consider it a “movement”. I can then keep track of movements and when enough has happened, I can consider it a shake. Obviously this can be tweaked. You would need to test on a real device and see what “feels” right. Here is the updated code with that logic in place:

import {Component} from [email protected]/core';
import {NavController,Platform} from 'ionic-angular';
import {CatProvider} from '../../providers/cat-provider/cat-provider';
import {DeviceMotion} from 'ionic-native';

@Component({
  providers: [CatProvider],
  templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

  public cats:Array<Object>;
  private lastX:number;
  private lastY:number;
  private lastZ:number;
  private moveCounter:number = 0;

  constructor(public catProvider:CatProvider, private navController: NavController, platform:Platform) {
    this.loadCats();

    platform.ready().then(() => {
      var subscription = DeviceMotion.watchAcceleration({frequency:200}).subscribe(acc => {
        //console.log(acc);

        if(!this.lastX) {
          this.lastX = acc.x;
          this.lastY = acc.y;
          this.lastZ = acc.z;
          return;
        }

        let deltaX:number, deltaY:number, deltaZ:number;
        deltaX = Math.abs(acc.x-this.lastX);
        deltaY = Math.abs(acc.y-this.lastY);
        deltaZ = Math.abs(acc.z-this.lastZ);

        if(deltaX + deltaY + deltaZ > 3) {
          this.moveCounter++;
        } else {
          this.moveCounter = Math.max(0, --this.moveCounter);
        }

        if(this.moveCounter > 2) { 
          console.log('SHAKE');
          this.loadCats(); 
          this.moveCounter=0; 
        }

        this.lastX = acc.x;
        this.lastY = acc.y;
        this.lastZ = acc.z;

      });
    });

  }

  loadMore() {
    console.log('load more cats');
    this.loadCats();
  }

  loadCats() {
    this.catProvider.load().then(result => {
      this.cats = result;
    });
  }

}

You can find the complete source code for this up on GitHub.

  • Jorge Cacho

    Thanks for the post!!

    I’m trying to use Ripple in Visual Studio Code but not able to find the way .. Do you have any tutorial showing how to do this? Thanks!

  • k3x

    Thanks for the post.
    Is there a tutorial wich talks about SQLite plugin from ionic-native to use a pre populated database?

    • http://www.raymondcamdencom/ Raymond Camden

      Speaking for myself, not Ionic as a whole, I am planning on writing one, I just can’t say when. I’ve got another one for Ionic Native in the works right now, and my plan was to work on a SQLite demo after it’s done.

      • k3x

        Thanks.
        How to get notified when this article becomes available?
        I’m trying to use a pre populated database with 20K rows and so far no luck the DB exists but doesn’t contain any table even if it has 7 or 8 tables and one of them has almost 20K rows.
        I’ve searched everywhere(mostly on ionic forum and stackoverflow) and people keep saying to populate it at the beginning, and i i’m like “are this guys even serious?”.
        To use a pre populated database we have to use the File from ionic-native and copy the DB from www/ to another folder? If so wich folder?
        Sorry to bother with so many questions but i’ve been trying to get this working for days.

        • http://www.raymondcamdencom/ Raymond Camden

          Well, in this article here, Ionic copied (with my permission!) my blog article from raymondcamden.com. That’s where I’ll be publishing again, so you can check or subscribe there. I assume the Ionic folks may copy that article too (again, with my permission and blessing).

          To your comments about being directed to do the inserts on startup – that’s because WebSQL by itself doesn’t support using a file. Being able to use a pre-installed DB out of the box is something SQLite can make better.

          Anyway – I’m a week or two away from even starting this research, so that’s all I can offer myself now.

          • k3x

            Will be checking your blog for updates.
            Thanks

        • http://www.raymondcamdencom/ Raymond Camden

          So I did a bit of research into this. The folks who made the SQLite plugin made 2 versions. One ‘base’ model and one that supports extra features, like shipping a prepopulated db. To me, this seems a bit silly, but what do I know.

          That being said – Ionic Native wraps the *basic* one, which probably makes sense, but means your use case is *not* supported by Ionic Native. Of course, you can *still* use it in your Ionic 1/2 apps! It just means you don’t get the nice syntax sugar that IN gives you.

          So I still plan on blogging on this, but I won’t be able to show *this* particular feature.

          However…

          Do note that you can write logic to do some of this for you. For example, you can ship an XML file of data, parse it on initial load, and insert data. I’ve done that before for basic WebSQL demos. Whether or not this makes sense depends on your initial data set. You would need to do some testing to see how much of an impact it has on the ‘first run’ experience. And obviously you can just run it in the background while the user does other stuff. And again – whether or not this makes sense depends on your data and your app.

          • k3x

            Thanks for your response.
            My database has the shcedules for metro/train for the entire year.
            Isn’t for situations like this that we have databases? To store data beforehand. Not all apps can be made without previous data, it’s my case.

          • http://www.raymondcamdencom/ Raymond Camden

            “Isn’t for situations like this that we have databases?”
            Sure. So use the other plugin and skip Ionic Native. Or – look at the size of the data. It may be fine to do the Inserts on app startup on the first run.

          • k3x

            Yes i’m looking on the other plugin right now but still facing the issue of the location of the file.
            Thanks for your help.

  • CannyCookie

    `Thats really useful and it works 🙂 Can anyone suggest how to implement subscription.unsubscribe(); when the page is navigated away from and then restarted when the user returns to this page?

    • http://www.raymondcamdencom/ Raymond Camden

      That’s a great question. Let me update the demo with a new blog post. Can’t promise it today, but this week.

    • http://www.raymondcamdencom/ Raymond Camden

      As an FYI, you are 100% correct this is an issue. Working on the update today. Will blog it at raymondcamden.com, share the link here. Ionic may republish it too.