"So you want to build a native app?"

2014

Hybrid Apps!

  • HTML5 that acts like native

  • Direct access to native APIs

  • Familiar web dev environment

  • single code base 

"I have heard hybrid apps are slow!"

“It's not 2007 anymore”

Mobile devices have rapidly improved!

 

Year Device Processor Ram
2007 iPhone 1   620 MHz                 128 MB

Mobile devices have rapidly improved!

 

Year Device Processor Ram
2007 iPhone 1 620 MHz 128 MB
2010 iPhone 4   1 GHz                     
 
512 MB

Mobile devices have rapidly improved!

 

Year Device Processor Ram
2007 iPhone 1 620 MHz 128 MB
2010 iPhone 4 1 GHz 512 MB
2014 iPhone 6   1.4 GHz dual-core 1 GB

Mobile devices have rapidly improved!

 

Year Device Processor Ram
2007 iPhone 1 620 MHz 128 MB
2010 iPhone 4 1 GHz 512 MB
2014 iPhone 6 1.4 GHz dual-core 1 GB
2015 iPhone 6s 1.8 GHz dual-core 2 GB

WEB TECHNOLOGIES YOU ALREADY

KNOW & LOVE

(You'll feel right at home)

SUPERPOWERED BY

ANGULAR

 

Extends the HTML vocabulary

Proven for  large-scale app development

UI Components using  Directives & Services

Sass!

CSS generated from the Sass preprocessor

Quickly give your app its own look and feel

CSS designed to be easily overridden

Supported Devices

iOS 6+

Android 4+

Win Phone 10

How it all comes together

  • Your App
  • Ionic
  • Angular
  • WebView (Cordova)
  • Native App

Ionic Components 

Demo

What are we building today?

ANGULAR

OVERVIEW

 

Quick Introduction

to the Basics


    angular.module(
        'todo', 
        [
          'ionic',
          'todo.services',
          'backand',
          'todo.backand',
          'todo.config.constants',
          'todo.interceptors'
        ]
    );

www\js\app.js

Module In Action


    angular.module(
        'todo', 
        [
          'ionic',
          'todo.services',
          'backand',
          'todo.backand',
          'todo.config.constants',
          'todo.interceptors'
        ]
    );

    <body ng-app="todo">

www\js\app.js & www\index.html

Module In Action


     angular
       .module('todo')
       .config(config);

     config.$inject = ['BackandProvider', '$stateProvider', 
            '$urlRouterProvider', '$httpProvider', 'CONSTS'];

     function config(
         BackandProvider, 
         $stateProvider, 
         $urlRouterProvider, 
         $httpProvider, 
         CONSTS) {

            BackandProvider.setSignUpToken(CONSTS.signUpToken);
            BackandProvider.setAppName(CONSTS.appName);

            $httpProvider.interceptors.push('APIInterceptor');
     })

www\js\config\app.config.js

Config In Action

$stateProvider
  .state('login', {
    url: '/login',
    templateUrl: 'templates/login.html',
    controller: 'LoginController as login'
  })

www\js\config\app.config.js

Routes In Action

.state('tab', {
  url: '/tab',
  abstract: true,
  templateUrl: 'templates/tabs.html'
})

www\js\config\app.config.js

Routes In Action

.state('tab.projects', {
  url: '/projects',
  views: {
     'tab-projects': {
      templateUrl: 'templates/tab-projects.html',
      controller: 'ProjectsController as vm'
    }
  }
})

www\js\config\app.config.js

.state('tab', {
  url: '/tab',
  abstract: true,
  templateUrl: 'templates/tabs.html'
})

Routes In Action

<ion-tab 
    title="Projects" 
    icon="ion-document" 
    href="#/tab/projects">

    <ion-nav-view 
        name="tab-projects">
    </ion-nav-view>
</ion-tab>

www\js\config\app.config.js

.state('tab.projects', {
  url: '/projects',
  views: {
     'tab-projects': {
      templateUrl: 'templates/tab-projects.html',
      controller: 'ProjectsController as vm'
    }}
})

Tab In Action

<ion-tab 
    title="Projects" 
    icon="ion-document" 
    href="#/tab/projects">

    <ion-nav-view 
        name="tab-projects">
    </ion-nav-view>
</ion-tab>

www\js\config\app.config.js

.state('tab.projects', {
  url: '/projects',
  views: {
     'tab-projects': {
      templateUrl: 'templates/tab-projects.html',
      controller: 'ProjectsController as vm'
    }}
})

Tab In Action

www\js\config\app.config.js

Default Route

State to show when user hits home page

 


    $urlRouterProvider.otherwise('/projects');



    $urlRouterProvider.otherwise('/projects');

    $urlRouterProvider.otherwise(function ($injector) {
    
        var $state = $injector.get("$state");
        $state.go("tab.projects");

    });

www\js\config\app.config.js

Default Route

State to show when user hits home page

 

Anatomy of A View

<ion-view view-title="Projects">

    <ion-content>
        <!-- Content Here -->
    </ion-content>

</ion-view>

www\templates\tab-projects.html

  • Grids

  • Menus         

  • Tabs   

  • ng-click

  • ng-options

  • ng-repeat

Examples


    <ion-list>
      <ion-item class="item item-text-wrap" 
            ng-repeat="project in vm.projects | orderBy:'name'" 
            ui-sref="tab.tasks({
                  projectId: project.id,
                  projectName: project.name
              })">

            <h2>{{project.name}} </h2>
            <p>created: {{project.created_on}}</p>

             <ion-option-button 
                class="button-assertive "
                ng-click="vm.deleteProject(project) ">
                  Delete
            </ion-option-button>

      </ion-item>
    </ion-list>

www\templates\tab-projects.html

ion-list Directive



    (function () {
      'use strict';

      angular
        .module('todo')
        .controller('ProjectsController', ProjectsController);

      ProjectsController.$inject = [
            'ProjectService', '$ionicModal', 
            '$state', '$scope', 
            '$ionicListDelegate', 'projects'];

      function ProjectsController(ProjectService, $ionicModal, 
            $state, $scope, 
            $ionicListDelegate, projects) {
        }

    })();

www\js\controllers\projects.controller.js

Controller

$scope

Glue between the controller and view

 

 

 

$scope - $broadcast and $on

  • $broadcast - fire event to selected scope 
     

  • $on - can receive the event
     

  • $rootScope - send event to the whole app

 



    function getMoreProjects() {
        ...

        $scope.$broadcast('scroll.infiniteScrollComplete')
    }

    function doRefresh() {
        ...

        $scope.$broadcast('scroll.refreshComplete');
    }


www\js\controllers\projects.controller.js

$scope

Types of Services

constant store values that will never change
(function () {
  angular.module('todo.config.constants', [])

    .constant('CONSTS', {
      anonymousToken: 'ec9b4565-8d2e-4740-a4dc-4e8e52dbbc72',
      signUpToken: '536d7143-edf0-451b-9e8d-fa065c563eb6',
      appName: 'ddjtodo'
    })

    .constant('$ionicLoadingConfig', {
      template: '<ion-spinner></ion-spinner>'
    })

})();

www\js\config\app.constants.js

constant

Types of Services

constant store values that will never change
factory Service created using constructor function

Uses Revealing Module pattern

        angular
            .module('todo.services')
            .factory('ProjectService', ProjectService);

        ProjectService.$inject = [];

        function ProjectService() {

           var service = {
           };
        
           return service;
           
        }

www\js\services\projects.service.js

Factory


    angular.module('todo.services')
        .factory('ProjectService', ProjectService);

    ProjectService.$inject = [
        '$http', 'BackandDataService', 'UserModel'
    ];

    function ProjectService($http, BackandDataService, UserModel) {
        var service = {
          getProjects: getProjects,
          addProject: addProject,
          deleteProject: deleteProject,
        };

        return service;

        function getProjects(pageNumber, pageSize) {}
        function addProject(name) {}
        function deleteProject(project) {}
    }

www\js\services\projects.service.js

Revealing Module

Types of Services

constant store values that will never change
service Service created using constructor function.

using the this keyword and more OOP-like
factory Service created using constructor function

Uses Revealing Module pattern

API Interceptors

(service)


      angular.module('todo.interceptors')
        .service('APIInterceptor', ApiInterceptor);

      ApiInterceptor.$inject = ['$rootScope', '$q'];

      function ApiInterceptor($rootScope, $q) {

        var service = this;
        service.responseError = responseError;
        service.request = request;
        service.response = response;
    
        function request(config) { return config }    
        function response(response) { return response; }
        function responseError(response) { 
            return $q.reject(response); 
        }

    }

www\js\interceptors\api.interceptor.js

Capture Errors



    function request(config) {
        $rootScope.$broadcast('loading:show');
        return config;
    }
    
    function response(response) {
        $rootScope.$broadcast('loading:hide');
        return response;
    }

    function responseError(response) {
        $rootScope.$broadcast('loading:hide');
        return $q.reject(response);
    }
    

www\js\interceptors\api.interceptor.js

Global Loading...

    angular
      .module('todo')
      .config(config);

    config.$inject = [
        'BackandProvider', '$stateProvider', 
        '$urlRouterProvider', '$httpProvider', 'CONSTS'];

    function config(
        BackandProvider, $stateProvider, 
        $urlRouterProvider, $httpProvider, CONSTS) {

        $httpProvider.interceptors.push('APIInterceptor');

    }

www\js\config\app.config.js

Using Interceptor

Must Read

  •  Powerful backend-as-a-service

  • Auto-Generated REST API 

  • Security & User Management

  • Server-Side Logic

Text

Visual Data Model

Json Data Model

Generated RestApi

return $http ({
  method: 'GET',
  url: Backand.getApiUrl() + '/1/objects/project',
  params: {
    pageSize: 20,
    pageNumber: 1,
    filter: null,
    sort: ''
  }
});

User Management


    Backand.signin(email, password);

www\js\services\login.service.js

Back& User Api


    Backand.signin(email, password);

    Backand.signup(
        firstName, lastName, 
        email, password, confirmPassword);

www\js\services\login.service.js

Back& User Api


    Backand.signin(email, password);

    Backand.signup(
        firstName, lastName, 
        email, password, confirmPassword);

    Backand.signout();

www\js\services\login.service.js

Back& User Api


    Backand.signin(email, password);

    Backand.signup(
        firstName, lastName, 
        email, password, confirmPassword);

    Backand.signout();

    Backand.socialSignIn(provider);

www\js\services\login.service.js

Back& User Api


    Backand.signin(email, password);

    Backand.signup(
        firstName, lastName, 
        email, password, confirmPassword);

    Backand.signout();

    Backand.socialSignIn(provider);

    Backand.socialSignUp(provider);

www\js\services\login.service.js

Back& User Api


    Backand.signin(email, password);

    Backand.signup(
        firstName, lastName, 
        email, password, confirmPassword);

    Backand.signout();

    Backand.socialSignIn(provider);

    Backand.socialSignUp(provider);

    Backand.getUserDetails()

www\js\services\login.service.js

Back& User Api


    Backand.signin(email, password);

    Backand.signup(
        firstName, lastName, 
        email, password, confirmPassword);

    Backand.signout();

    Backand.socialSignIn(provider);

    Backand.socialSignUp(provider);

    Backand.getUserDetails()

    Backand.getToken()

www\js\services\login.service.js

Back& User Api

Lets Get Coding

Sticky Notes

Done With Exercise

 

Have Question

 

Need Help 

 

Starting ionic project

$ ionic serve

demo@demo.com

demo12

Open Project In VSCode

Making Master/Detail 
Obvious



<ion-item 

          class="item item-text-wrap" 

          ng-repeat="project in vm.projects | orderBy:'name'" 
          ui-sref="tab.tasks({ 
            projectId: project.id, projectName: project.name })" 
          ng-style="{'line-height': '50px'}"
        >
        <h2>{{project.name}} </h2>

  18        <i class="ion-chevron-right icon"></i>

        <p>created: {{project.created_on}}</p>
        <ion-option-button class="button-assertive "
                       ng-click="vm.deleteProject(project) ">
            Delete
        </ion-option-button>
</ion-item>

www\templates\tab-projects.html

Adding > icon



<ion-item 

  11      class="item item-text-wrap item-icon-right" 

          ng-repeat="project in vm.projects | orderBy:'name'" 
          ui-sref="tab.tasks({ 
                projectId: project.id, projectName: project.name })" 
          ng-style="{'line-height': '50px'}"
        >
        <h2>{{project.name}} </h2>

        <i class="ion-chevron-right icon"></i>

        <p>created: {{project.created_on}}</p>
        <ion-option-button class="button-assertive "
                       ng-click="vm.deleteProject(project) ">
            Delete
        </ion-option-button>
</ion-item>

www\templates\tab-projects.html

Adding > icon



<ion-item 

          class="item item-text-wrap item-icon-right" 

          ng-repeat="project in vm.projects | orderBy:'name'" 
          ui-sref="tab.tasks({ 
                projectId: project.id, 
                projectName: project.name })" 
          ng-style="{'line-height': '50px'}"
        >
        <h2>{{project.name}} </h2>

  18    <i class="ion-chevron-right icon icon-accessory"></i>

        <p>created: {{project.created_on}}</p>
        <ion-option-button class="button-assertive "
                       ng-click="vm.deleteProject(project) ">
            Delete
        </ion-option-button>
</ion-item>

www\templates\tab-projects.html

Adding > icon

Add New Task




<ion-view enable-menu-with-back-views="true">
    <ion-nav-title>Tasks: {{vm.project.name}}
    </ion-nav-title>

  4    <ion-nav-buttons side="right">

            <button 
                    class="button button-icon ion-compose" 
                    ng-click="vm.showTaskModal()">       
            </button>

        </ion-nav-buttons>

    <ion-content>
    ...
    <ion-content>
</ion-view>

www\templates\tab-project-tasks.html

Task Adding + icon





  34  function activate() {    
    
        $ionicModal
            .fromTemplateUrl('templates/modal-new-task.html', {
                scope: $scope
            }).then(function(modal) {
                vm.taskModal = modal;
            });

      }

www\js\controllers\tasks.controller.js

Wire Up Modal





  96    function showTaskModal() {

            vm.taskModal.show();

        }
 
        function closeTaskModal() {

            vm.taskModal.hide();

        }


        $scope.$on('$destroy', function () {
          vm.taskModal.remove();
        });

www\js\controllers\tasks.controller.js

Wire Up Modal


  82

    function saveNewTask(task) {

      TaskService.addTask(vm.project, task)
        .then(function (result) {
        
              vm.tasks.push(result);

              closeTaskModal();
      
              task.name = '';

      });

    }

www\js\controllers\tasks.controller.js

Wire Up Save 

Delete Task


  12

<ion-item 
    class="item-text-wrap item item-icon-right" 
    ng-repeat="task in vm.tasks | orderBy: ['completed','name']">

        {{task.name}}
         
        <i class="ion-close icon icon-accessory"
            ng-click="vm.deleteTask(task)"></i>

      </ion-item>

www\templates\tab-project-tasks.html

Delete Button


  100


     function deleteTask(task) {

        TaskService.deleteTask(vm.project, task)
            .then(function (result) {  
        
              vm.tasks.splice(vm.tasks.indexOf(task), 1);

         });

    }

www\js\controllers\tasks.controller.js

Wire Up Delete

Complete Task


  12

<ion-item 
    class="item-text-wrap item  item-icon-left item-icon-right" 
    ng-repeat="task in vm.tasks | orderBy: ['completed','name']">

        {{task.name}}

        <i class="icon"
            ng-class="task.completed ? 
                'ion-checkmark-circled' : 'ion-ios-circle-outline'"
            ng-click="vm.completeTask(task, $index)">
        </i>

        <i class="ion-close icon icon-accessory"
            ng-click="vm.deleteTask()"></i>

</ion-item>

www\templates\tab-project-tasks.html

Complete Button


  96


     function completeTask(task) {

        TaskService.completeTask(vm.project, task)
            .then(function (result) {

        });

    }

www\js\controllers\tasks.controller.js

Wire Up Complete

Pull to Refresh


  10


  <ion-content>

    <ion-refresher
        pulling-text="Pull to refresh..."
        on-refresh="vm.doRefresh()">
    </ion-refresher>

    <ion-list ng-if="vm.tasks.length > 0">

www\templates\tab-project-tasks.html

Refresher


  62

    function doRefresh() {

        vm.refreshing = true;
        vm.pageNumber = 1;

        TaskService.getTasks(vm.project, vm.pageNumber, vm.pageSize)
            .then(function (result) {          
              vm.tasks = result;
        })

        .finally(function () {

            $scope.$broadcast('scroll.refreshComplete');

            vm.refreshing = false;

        });
    }

www\js\controllers\tasks.controller.js

Wire Up Refresh

Infinite Scroll


  21

<ion-infinite-scroll 

        on-infinite="vm.getMoreTasks() "

        ng-if="vm.moreDataCanBeLoaded 
                && !vm.refreshing 
                && vm.tasks.length > 0" 

        immediate-check="false" 

>
</ion-infinite-scroll>

www\templates\tab-project-tasks.html

Wire Up Infinite Scroll


  43

    function getMoreTasks() {

      vm.pageNumber = vm.pageNumber + 1;

      TaskService.getTasks(vm.project, vm.pageNumber, vm.pageSize)
        .then(function (result) {
          var rowNum = result.length;
          if (rowNum === 0 || rowNum < vm.pageSize) {
            vm.moreDataCanBeLoaded = false;
          }

          if (rowNum > 0) {
            vm.tasks = vm.tasks.concat(result);
          }
        })
        .finally(function () {
          $scope.$broadcast('scroll.infiniteScrollComplete')
        });
    }

www\js\controllers\tasks.controller.js

Wire Up Get More

Profile Page


  10


    <ion-tab title="Profile" 
        icon="ion-ios-person" 
        href="#/tab/profile">
        
            <ion-nav-view name="tab-profile"></ion-nav-view>
    </ion-tab>

www\templates\tabs.html

Add Tab


  66


  .state('tab.profile', {
      url: '/profile',
      views: {
        'tab-profile': {
          templateUrl: 'templates/tab-profile.html',
          controller: 'ProfileController as vm',
        }
      }
    })

www\js\config\app.config.js

Add Route

Confirm

Before

Task Delete


  103

    TasksController.$inject = ['TaskService', '$stateParams', 
        '$ionicModal', '$scope', 'tasks', 
        '$ionicPopup'];
    function TasksController(TaskService, $stateParams, 
        $ionicModal, $scope, tasks, 
        $ionicPopup) {
        ....
        function deleteTask(task) {
            $ionicPopup.confirm({
                title: 'Are You Sure?',
                template: 'Are you sure you want to delete this task?'
            }).then(function (res) {
                if (res) {

                    TaskService.deleteTask(vm.project, task)
                        .then(function (result) {                     
                        vm.tasks.splice(vm.tasks.indexOf(task), 1);
                    });

                }
            });
        }

www\js\tasks.controller.js

ionic popup

Should Also 
Add to 
Project Delete

Theming

  1. Stop ionic serve

  2. Run ionic setup sass

 



/*
$light:                           #fff !default;
$stable:                          #f8f8f8 !default;
$positive:                        #387ef5 !default;
$calm:                            #11c1f3 !default;
$balanced:                        #33cd5f !default;
$energized:                       #ffc900 !default;
$assertive:                       #ef473a !default;
$royal:                           #886aea !default;
$dark:                            #444 !default;
*/

scss\ionic.app.scss

Theme Colors

$calm: pink !default;

  15

<ion-item 
    class="item-text-wrap item item-icon-left  item-icon-right" 
    ng-repeat="task in vm.tasks | orderBy: ['completed','name']"
        
    ng-class="{completedTask: task.completed}"

>

    {{task.name}}

    <i class="icon"
        ng-class="task.completed ? 
            'ion-checkmark-circled' : 'ion-ios-circle-outline'"
          ng-click="completeTask(task, $index)">
    </i>
    <i class="ion-close icon icon-accessory"
        ng-click="vm.deleteTask()"></i>
</ion-item>

www\templates\tab-project-tasks.html

Complete Class


  50


.completedTask {
	text-decoration: line-through;
	color: #A9A9A9;	
}

scss\app.scss

Complete SCSS

Gulp Tasks

  • Add css & js reference tags 

  • Compile Sass  

Ionic Tooling

Automatically generate icons and splash screens

 

Creates size needed for each platform

 

 

Icons and Splash Screens

$ ionic resources
$ ionic resources --icon
$ ionic resources --splash

Platform Setup

VS Code Snippets

200+ Snippets 

700 icons

Resources

Blogs

Time to Go

Create Your

Mobile App

thank you

http://bitly.com/fluent-ionic-slides

 

@digitaldrummerj 

http://digitaldrummerj.me

Fluent Conf Ionic Workshop

By Justin James

Fluent Conf Ionic Workshop

Workshop for Ionic

  • 3,253