Building Hybrid Apps with AngularJS and Ionic

Overview

  1. Why Ionic?
  2. Ionic Technology Stack
  3. Ionic Components
  4. Angular Shallow Dive
  5. Building out a Demo Application
  6. Access Native Device Functionality
  7. AngularJS Primer
“I want to build an app!”

More Platforms. More Problems.

  • Proficiency in each platform required
  • Entirely separate code bases
  • Timely & expensive development
  • Diminishing returns

Hybrid Apps!

HTML5 that acts like native

Web wrapped in native layer

Direct access to native APIs

Familiar web dev environment

A single code base (web platform!)

“It's not 2007 anymore”
Year Device Processor RAM
2007 iPhone 400 MHz 128 MB
2010 iPhone 4 1 GHz 512 MB
2015 iPhone 6 1.4 GHz dual-core 1 GB

Web Technologies You Already

Know & Love

(You'll feel right at home)

States Managed by UI Router


angular.module('ionicApp', ['ionic'])

.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider

  .state('app', {
    url: '/app',
    abstract: true,
    templateUrl: 'templates/menu.html',
    controller: 'AppCtrl'
  })

  .state('app.search', {
    url: '/search',
    views: {
      'menuContent': {
        templateUrl: 'templates/search.html'
      }
    },
  })

  .state('app.browse', {
    url: '/browse',
    views: {
      'menuContent': {
        templateUrl: 'templates/browse.html',
        controller: 'BrowseCtrl'
      }
    },
  })

  $urlRouterProvider.otherwise('/app/browse');
});
        

State-Based Router with Nested Views

Your own Components


<div class="list card">
  <div class="item item-body card-body">
    <div class="image-container">
      <img src="{{postData.image}}" alt="" />
    </div>
    <div class="content-container">
      <div class="description">{{postData.description}}</div>
      <div class="" ng-if="postData.comments.length">
        <button class="button button-clear button-positive" ng-click="toggleComments()">
          ...
        </button>
        <div class="item item-text-wrap" ng-show="commentsVisible" ng-repeat="comment in postData.comments track by $index">
          {{comment}}
        </div>
      </div>
      <div ng-show="commentFieldVisible">
        <div class="item item-input-inset" ng-show="commentFieldVisible">
          <label class="item-input-wrapper">
            <input type="text" placeholder="Add a comment" ng-model="newComment">
          </label>
          <button class="button button-small button-balanced" ng-click="addComment()">Submit</button>
        </div>
      </div>
      <div class="button-bar">
        ...
      </div>
    </div>
  </div>
</div>
        

It's just Angular.js! (No extra learning curve)


.directive('postCard', function() {
  return {
      restrict: 'E',
      templateUrl: 'templates/post-card.html',
      scope: {
        postData: "=",
      },
      controller: function($scope, $element) {
        // Some controller logic
      }
  };
});

.controller('BrowseCtrl', function($scope) {
  $scope.posts = [
    { image: 'img/arya.jpg', description: "" },
    { image: 'img/jon-snow.jpg', description: "" },
    { image: 'img/tyrion.jpg', description: "" },
    { image: 'img/daenerys.jpg', description: "" },
  ];
})
        

  ...
  <ion-content>
    <post-card ng-repeat="post in posts" post-data="post"></post-card>
  </ion-content>
  ...
        

Required Tools

  • NodeJS (https://nodejs.org/en/)
  • Bower (http://bower.io/#install-bower)
  • Ionic & Cordova (http://ionicframework.com/docs/guide/installation.html)

npm install -g ionic cordova

Boilerplate app structure ready for customization

LiveReload both local and native builds

Build and run native apps

Helpful Ionic CLI Commands

  • ionic setup sass
  • ionic serve (browser)
  • ionic serve -l (launches Lab)
  • If you have platform tools installed (iOS & Android SDKs):
    • ionic emulate (Ionic adds iOS platform and builds for iOS by default)
    • To build & run using Android SDK:
      • ionic platform add android
      • ionic build android
      • ionic run android

Building out our app

js/app.js

angular.module('ionicApp', ['ionic'])

.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider
  .state('app', {
    url: '/app',
    abstract: true,
    templateUrl: 'templates/menu.html',
    controller: 'AppCtrl'
  })

  .state('app.browse', {
    url: '/browse',
    views: {
      'menuContent': {
        templateUrl: 'templates/browse.html',
        controller: 'BrowseCtrl'
      }
    },
  });
  // if none of the above states are matched, use this as the fallback
  $urlRouterProvider.otherwise('/app/browse');
})

.controller('AppCtrl', function($scope) {})

.controller('BrowseCtrl', function($scope) {});


                        
/templates/browse.html

<ion-view view-title="Browse">
  <ion-content>
    <h2>Browse</h2>
  </ion-content>
</ion-view>
            
/templates/menu.html

<ion-side-menus enable-menu-with-back-views="false">
  <ion-side-menu-content>
    <ion-nav-bar class="bar-positive">
      <ion-nav-back-button>
      </ion-nav-back-button>

      <ion-nav-buttons side="left">
        <button class="button button-icon button-clear ion-navicon" menu-toggle="left">
        </button>
      </ion-nav-buttons>
    </ion-nav-bar>
    <ion-nav-view name="menuContent"></ion-nav-view>
  </ion-side-menu-content>

  <ion-side-menu side="left">
    <ion-content>
      <ion-list>
        <ion-item menu-close href="#/app/browse">
          Browse
        </ion-item>
      </ion-list>
    </ion-content>
  </ion-side-menu>
</ion-side-menus>
            

Managing and Abstracting our view logic

/templates/post-card.html

<div class="list card">
  <div class="item item-body card-body">
    <div class="image-container">
      <img src="{{postData.image}}" alt="" />
    </div>
    <div class="content-container">
      <div class="description">{{postData.description}}</div>
      <div class="" ng-if="postData.comments.length">
        <button class="button button-clear button-positive" ng-click="toggleComments()">
          ...
        </button>
        <div class="item item-text-wrap" ng-show="commentsVisible" ng-repeat="comment in postData.comments track by $index">
          {{comment}}
        </div>
      </div>
      <div ng-show="commentFieldVisible">
        <div class="item item-input-inset" ng-show="commentFieldVisible">
          <label class="item-input-wrapper">
            <input type="text" placeholder="Add a comment" ng-model="newComment">
          </label>
          <button class="button button-small button-balanced" ng-click="addComment()">Submit</button>
        </div>
      </div>
      <div class="button-bar">
        ...
      </div>
    </div>
  </div>
</div>
                        
/js/app.js

.controller('BrowseCtrl', function($scope) {
  $scope.posts = [
    { image: 'img/arya.jpg', description: "..." },
    { image: 'img/jon-snow.jpg', description: "..." },
    { image: 'img/tyrion.jpg', description: "..." },
    { image: 'img/daenerys.jpg', description: "..." },
  ];
})
            
/templates/browse.html

<ion-view view-title="Browse">
  <ion-content class="animate-fade-slide-in">
    <post-card ng-repeat="p in posts" post-data="p"></post-card>
  </ion-content>
</ion-view>
            
/js/app.js

.directive('postCard', function($ionicActionSheet, $ionicPopup, $timeout) {
    return {
        restrict: 'E',
        templateUrl: 'templates/post-card.html',
        scope: {
          postData: "=",
        },
        controller: function($scope, $element) {
          $scope.commentsVisible = false;
          $scope.commentFieldVisible = false;
          $scope.postData.comments = $scope.postData.comments || [];

          $scope.toggleLike = function() { ...

          $scope.addComment = function() { ...

          $scope.showOptions = function() { ...

        }
    };
});
            

Accessing and Managing our Server Side Data

Providers and Service Objects

js/app.js

.factory('Post', function($resource) {
  return $resource('http://my-awesome-REST-API.com/post');
})
                        
js/app.js

.controller('BrowseCtrl', function($scope, Post) {
  // Get all posts
  $scope.posts = Post.query();
})
                        
1. Install ngResource via Bower (optional)

  $ bower install ng-resource --save
            
2. Add dependency to index.html (optional)

 <script src="lib/ng-resource/<path to>/ng-resource.js"></script>
            
3. Include in Angular app dependencies (optional)

 angular.module('ionicApp', ['ionic', 'ngResource'])
            
AdMob Device Keychain Social Sharing
App Availability Device Motion NativeAudio Spinner
BackgroundGeolocation Device Orientation Media Splashscreen
Battery Status Dialogs Local Notification SQLite
Barcode Scanner File Network Statusbar
Camera Flashlight OAuth Toast
Capture Geolocation Pin Dialog Touch ID
Clipboard Globalization Printer Vibration
Contacts GoogleAnalytics Progress Indicator Zip
DatePicker Keyboard Push Notifications More...

Access Native Device Functionality

  • Install ngCordova
  • Include ngCordova JS in index.html (after bundled ionic/angularjs)
  • Inject ngCordova service
  • Install the Cordova Camera plugin

bower install ngCordova --save-dev

lib/ngCordova/dist/ng-cordova.js

angular.module('starter', ['ionic','ngCordova'])

ionic plugin add org.apache.cordova.camera

ngCordova Example


.controller('AppCtrl', function($scope, $cordovaCamera) {

  document.addEventListener("deviceready", function () {

    var options = {
      quality: 50,
      destinationType: Camera.DestinationType.DATA_URL,
      sourceType: Camera.PictureSourceType.CAMERA,
      allowEdit: true,
      encodingType: Camera.EncodingType.JPEG,
      targetWidth: 100,
      targetHeight: 100,
      popoverOptions: CameraPopoverOptions,
      saveToPhotoAlbum: false,
      correctOrientation:true
    };

    $scope.getPicture = function() {
      $cordovaCamera.getPicture(options).then(function(imageData) {
        var image = document.getElementById('myImage');
        image.src = "data:image/jpeg;base64," + imageData;
      }, function(err) {
        // error
      });
    };

  }, false);
});
        

<ion-nav-buttons side="right">
  <button class="button button-icon button-clear ion-plus"
          menu-toggle="right"
          ng-click="getPicture()"></button>
</ion-nav-buttons>
        

Taking a photo

Coming Soon...

Get Started with Ionic!


Getting started guide
ionicframework.com/getting-started


Documentation
ionicframework.com/docs


Visit the Community Forum
forum.ionicframework.com


Contribute on GitHub
github.com/driftyco/ionic

</html>

@mobomoapps

http://withinsight.github.io/modev-ionic