Page 1 of 1

Hybrid Mobile Apps: Intro To AngularJS And Ionic Framework Rate Topic: -----

#1 Dogstopper  Icon User is offline

  • The Ninjaducky
  • member icon

Reputation: 2965
  • View blog
  • Posts: 11,222
  • Joined: 15-July 08

Post icon  Posted 20 April 2015 - 09:42 PM

Why Javascript?
In the mobile space, web apps are beginning to really pick up speed. The most recent OS's, iOS 8 and Android 5, support HTML 5 in their Safari and Chrome browsers and are extremely fast. And even those of last generation are really good. A demonstration by Unity Engine shows that even games built in the unity engine can be converted to native javascript using a tool called Emscripten. This basically takes LLVM code generated by C and C++ and converts it to a subset of Javascript known as asm.js. This brings a lot of power to the web that did not previously exist. On Firefox, it runs at an astonishing 75% of the speed of native apps. Try it out here: http://beta.unity3d....onas/AngryBots/

If you try it on a modern native browser, you will see that it renders (though the controls are not build in). Thus, we are getting to the point where we can even write complicated games in browsers. So Javascript exists, and it is everywhere. With that detour, I will finish this section with Atwood's Law, which states that any application that can be written in Javascript will eventually be written in Javascript.

Setting up a Hybrid App
All of this being said, the web space can be easily be moved to the mobile space - and I do not just mean visiting a website through a browser. I mean actually writing native apps that are coded in HTML5 and Javascript. This brings immense power to developers who only want to maintain a single codebase. This helps to make testing and and adding features easy, as they do not have to maintain several possibly huge codebases. In this tutorial, I will take you through setting up the ionic framework (http://ionicframework.com), which is an extension of AngularJS, and Cordova.

The first things that are needed are Node.js (https://nodejs.org/), which is available on Windows, MacOS, and Linux. Once you have that configured, use the familiar Node Package Manager to install ionic and cordova:
npm install -g cordova ionic



There are a bunch of optional packages that you can also install for support for stuff like emulating your app in the iOS simulator and the Android simulator. Note that even though this process will compile to any supported platform, you will still need a mac and XCode to compile to iOS.

Let's continue by taking you through one of the basic template apps. if you do the following, we will compile and deploy a template app:
ionic start myapp tabs
cd myapp
ionic platform add <ios|android>



At this point, the ionic tool will add all the dependencies for iOS or Android respectively. Make sure that you have XCode and the Android SDK installed in order for this to work. You should also have a bunch of folders and files that we will go into shortly. But first, let's compile and deploy:
ionic emulate <ios|android>


Will compile and deploy to the default emulator on your computer. If you want to deploy to a device, use:
ionic run <ios|android>



If you simply want to test in a browser, use:
ionic serve


Note, that with the last command, you will not be able to use cordova functionality, because it is designed to use features found in native apps.

These are the screens that we are going to focus on during this tutorial.
Dashboard:
Attached Image

Chats:
Attached Image

After running one of the previous three commands, you see an app with a native look and feel (iOS has tabs at the bottom of the page and Android will have them at the top). However, this app is actually HTML and Angular.js. For the rest of the tutorial, we'll go through the code that was generated and how it works. Please note that it assumes an basic understanding of Angular.js; however, if you know angular, we're basically done, as you should know the rest.

Diving In
Starting the www directory, we have index.html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title></title>

    <link href="lib/ionic/css/ionic.css" rel="stylesheet">
    <link href="css/style.css" rel="stylesheet">

    <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
    <link href="css/ionic.app.css" rel="stylesheet">
    -->

    <!-- ionic/angularjs js -->
    <script src="lib/ionic/js/ionic.bundle.js"></script>

    <!-- cordova script (this will be a 404 during development) -->
    <script src="cordova.js"></script>

    <!-- your app's js -->
    <script src="js/app.js"></script>
    <script src="js/controllers.js"></script>
    <script src="js/services.js"></script>
  </head>
  <body ng-app="starter">
    <!--
      The nav bar that will be updated as we navigate between views.
    -->
    <ion-nav-bar class="bar-stable">
      <ion-nav-back-button>
      </ion-nav-back-button>
    </ion-nav-bar>
    <!--
      The views will be rendered in the <ion-nav-view> directive below
      Templates are in the /templates folder (but you could also
      have templates inline in this html file if you'd like).
    -->
    <ion-nav-view></ion-nav-view>
  </body>
</html>



Within the <head> tag, we see some familiar information. The viewport settings for example causes the browser to set its width and height to be the same size as the device screen and accounts for system options like text size. It also removes the ability to zoom that you would have in a web browser, as this is generally not default behavior in a native app. This is a familiar tag for those who code responsive mobile websites. Also, in <head>, we pull in the ionic, cordova, and app CSS and JS files.

The meat of the app though is in the body. Here we use the directive ng-app, to specify which angular module to use initially. This is where we specify the <ion-nav-bar> custom element and set its css class (there are tons of these for ionic so check out their docs). The <ion-nav-view> element is where the content of the page will render. Next, let's look at app.js:
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])

.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if (window.StatusBar) {
      // org.apache.cordova.statusbar required
      StatusBar.styleLightContent();
    }
  });
})

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

  // Ionic uses AngularUI Router which uses the concept of states
  // Learn more here: https://github.com/angular-ui/ui-router
  // Set up the various states which the app can be in.
  // Each state's controller can be found in controllers.js
  $stateProvider

  // setup an abstract state for the tabs directive
    .state('tab', {
    url: "/tab",
    abstract: true,
    templateUrl: "templates/tabs.html"
  })

  // Each tab has its own nav history stack:

  .state('tab.dash', {
    url: '/dash',
    views: {
      'tab-dash': {
        templateUrl: 'templates/tab-dash.html',
        controller: 'DashCtrl'
      }
    }
  })

  .state('tab.chats', {
      url: '/chats',
      views: {
        'tab-chats': {
          templateUrl: 'templates/tab-chats.html',
          controller: 'ChatsCtrl'
        }
      }
    })
    .state('tab.chat-detail', {
      url: '/chats/:chatId',
      views: {
        'tab-chats': {
          templateUrl: 'templates/chat-detail.html',
          controller: 'ChatDetailCtrl'
        }
      }
    })

  .state('tab.account', {
    url: '/account',
    views: {
      'tab-account': {
        templateUrl: 'templates/tab-account.html',
        controller: 'AccountCtrl'
      }
    }
  });

  // if none of the above states are matched, use this as the fallback
  $urlRouterProvider.otherwise('/tab/dash');

});



Let's go through this piece by piece:
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])



This line specifies that this file is an angular module named starter (which we specified in the HTML file as ng-app), and that this module requires the ionic, starter.controllers, and the starter.services angular modules to be passed as arguments.

.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if (window.StatusBar) {
      // org.apache.cordova.statusbar required
      StatusBar.styleLightContent();
    }
  });
})



This is pretty self-explanatory. We wait for the ionic platform to be ready and then we set up the cordova modules that are needed for the app.

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

  // Ionic uses AngularUI Router which uses the concept of states
  // Learn more here: https://github.com/angular-ui/ui-router
  // Set up the various states which the app can be in.
  // Each state's controller can be found in controllers.js
  $stateProvider

  // setup an abstract state for the tabs directive
    .state('tab', {
    url: "/tab",
    abstract: true,
    templateUrl: "templates/tabs.html"
  })

  // Each tab has its own nav history stack:

  .state('tab.dash', {
    url: '/dash',
    views: {
      'tab-dash': {
        templateUrl: 'templates/tab-dash.html',
        controller: 'DashCtrl'
      }
    }
  })

  .state('tab.chats', {
      url: '/chats',
      views: {
        'tab-chats': {
          templateUrl: 'templates/tab-chats.html',
          controller: 'ChatsCtrl'
        }
      }
    })
  ...
  // if none of the above states are matched, use this as the fallback
  $urlRouterProvider.otherwise('/tab/dash');
});



This section of our code sets up the different screens that will be shown in the app. Basically, each time the screen changes, we change state. Each state has a short name and a url associated with it. The tab state is abstract, meaning it sets up a root url for each state that extends that state. Since every subsequent state extends from this one the template file, "tabs.html", will be included in each sub-state. This view is injected into index.html in the <ion-view> tag.

In the tabs.dash state, we extend from tabs state, specify a url (/tab/dash) which is used in the templates and controllers to change state. We also specify the views that are used in this state. In this example, there is one view called 'tab-dash', which has a template and controller.

Below this section, we have another state that occurs when the tab is changed to the chats tab. It in turn, specifies its URL, view and controller. The rest of the states I have omitted, as they follow a similar pattern.

Let's look at the templates. The tabs.html file appears as the following:
<!--
Create tabs with an icon and label, using the tabs-positive style.
Each tab's child <ion-nav-view> directive will have its own
navigation history that also transitions its views in and out.
-->
<ion-tabs class="tabs-icon-top tabs-color-active-positive">

  <!-- Dashboard Tab -->
  <ion-tab title="Status" icon-off="ion-ios-pulse" icon-on="ion-ios-pulse-strong" href="#/tab/dash">
    <ion-nav-view name="tab-dash"></ion-nav-view>
  </ion-tab>

  <!-- Chats Tab -->
  <ion-tab title="Chats" icon-off="ion-ios-chatboxes-outline" icon-on="ion-ios-chatboxes" href="#/tab/chats">
    <ion-nav-view name="tab-chats"></ion-nav-view>
  </ion-tab>

  <!-- Account Tab -->
  <ion-tab title="Account" icon-off="ion-ios-gear-outline" icon-on="ion-ios-gear" href="#/tab/account">
    <ion-nav-view name="tab-account"></ion-nav-view>
  </ion-tab>


</ion-tabs>



Here, we use the <ion-tabs> tag to specify a list of tabs. The class tabs-icon-top tells the tabs to display the tab's icon over the title of the tab. Inside of this tabs view, we list a set of tabs. Note that the title, icon-on, and icon-off are used for the appearance of the tab. The href is the glue that binds this view to the specific state that we want to use.

For the dashboard tab, we immediately recognize that /tab/dash is the url specified in the tabs.dash state. So now, we have an <ion-view> inside, which is named 'tab-dash'. Note that the state for this tab specifies a template and controller that will be used in this view. Without further ado, let's look at these files.

From starter.controllers module:
.controller('DashCtrl', function($scope) {})



In this case, there is nothing special, as the template file contains the example data. However, normally we would connect to a data source here. We will see an example of this in the tabs.chats state.

tab-dash.html:
<ion-view view-title="Dashboard">
  <ion-content class="padding">
    <div class="list card">
      <div class="item item-divider">Recent Updates</div>
      <div class="item item-body">
        <div>
          There is a fire in <b>sector 3</b>
        </div>
      </div>
    </div>
    <div class="list card">
      <div class="item item-divider">Health</div>
      <div class="item item-body">
        <div>
          You ate an apple today!
        </div>
      </div>
    </div>
    <div class="list card">
      <div class="item item-divider">Upcoming</div>
      <div class="item item-body">
        <div>
          You have <b>29</b> meetings on your calendar tomorrow.
        </div>
      </div>
    </div>
  </ion-content>
</ion-view>



This is pretty trivial HTML. It uses a lot of custom elements developed from ionic to make our styling be perfect for mobile. At the root <ion-view> we specify the title of the view, which is is displayed in the appropriate location based on the device. Everything within a <ion-content> is within a system scroll view. There are a lot of directives available for each, so again check out the docs for each element: http://ionicframewor...ive/ionContent/

Now, within the scroll view, we specify a bunch of divs that look like cards that should be familiar to users of Google Now or the Facebook apps. This is all done using pure CSS. As you can see, there is nothing needed from the controller, though in a real app, we might use an ng-repeat directive to pull the data from a list of objects specified in the controller.

Next, let's look at the tabs.chat state, which has more complicated behavior. We already showed how the tabs.html template connects a view with an associated template and controller, so let's skip to the controller.

From starter.controllers module:
.controller('ChatsCtrl', function($scope, Chats) {
  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
})



Here, we name our controller and specify the necessary dependencies. $scope allows us to bind data to the template and Chats is a factory that gives us the data needed for this controller. We bind a variable and a function to the $scope. The chats variable queries the factory's all function and binds that data. The remove function takes in a specific chat object and tells the factory to remove it from the data source.

Let's look at this factory, which can be thought of as a data provider within an MVC application, and is where an app will connect to a data source such as a SQL or Mongo database.
angular.module('starter.services', [])

.factory('Chats', function() {
  // Might use a resource here that returns a JSON array

  // Some fake testing data
  var chats = [{
    id: 0,
    name: 'Ben Sparrow',
    lastText: 'You on your way?',
    face: 'https://pbs.twimg.com/profile_images/514549811765211136/9SgAuHeY.png'
  }, 

    ...

  {
    id: 4,
    name: 'Perry Governor',
    lastText: 'Look at my mukluks!',
    face: 'https://pbs.twimg.com/profile_images/491995398135767040/ie2Z_V6e.jpeg'
  }];

  return {
    all: function() {
      return chats;
    },
    remove: function(chat) {
      chats.splice(chats.indexOf(chat), 1);
    },
    get: function(chatId) {
      for (var i = 0; i < chats.length; i++) {
        if (chats[i].id === parseInt(chatId)) {
          return chats[i];
        }
      }
      return null;
    }
  };
});



Here, we name a factory called Chats. A factory is a closure that returns an API to interact with internal data. Here, the functions that are invocable from our controllers are "all", "remove" and "get". The data that they are closing around could come from a database, information from Cordova, a promise, or hardcoded data, as we have done here. The returned functions operate on this internal data. If you are not familiar with Closures, they are a powerful way of having private data in Javascript (Read the following blog: http://toddmotto.com...script-scope/). As a quick side note, there are also services and providers which have slightly different behaviors than factories.

Now, as you can see from our controller, we have used two of these functions and bound them to $scope. Let's finish up this tutorial by looking at how this is bound to the template.

tab-chat.html
<ion-view view-title="Chats">
  <ion-content>
    <ion-list>
      <ion-item class="item-remove-animate item-avatar item-icon-right" ng-repeat="chat in chats" type="item-text-wrap" href="#/tab/chats/{{chat.id}}">
        <img ng-src="{{chat.face}}">
        <h2>{{chat.name}}</h2>
        <p>{{chat.lastText}}</p>
        <i class="icon ion-chevron-right icon-accessory"></i>

        <ion-option-button class="button-assertive" ng-click="remove(chat)">
          Delete
        </ion-option-button>
      </ion-item>
    </ion-list>
  </ion-content>
</ion-view>



You will note most of the same stuff as the last template. However, you see the ng-repeat="chat in chats" directive, which uses the $scope.chats variable to iterate through the list of chats and names each object chat. It also binds that <ion-item> to the tabs.chat-detail state, with the chat id provided. However, we will not go into that state in this tutorial. Within each item, we use the JS object chat to construct our view from the model. For reference, each chat has the following object:
{
    id: 0,
    name: 'Ben Sparrow',
    lastText: 'You on your way?',
    face: 'https://pbs.twimg.com/profile_images/514549811765211136/9SgAuHeY.png'
}



This was given to use through the factory. We use each item in the HTML using {} symbols. Anything within those symbols (often called moustaches or handlebars) uses the controller's bindings. So in the following HTML snippet, we construct a view using those attributes.
<img ng-src="{{chat.face}}">
<h2>{{chat.name}}</h2>
<p>{{chat.lastText}}</p>



We also add an option button, which is shown when panning on a button. Once this button is pressed, the remove(chat) function is called, which, in turn, tells the factory to remove that data. This is done using the ng-click="remove(chat)” directive.

That's about it! I encourage you to play around a little bit more in the sample program to learn how each screen is constructed. To summarize, Javascript is a powerful way to write mobile apps using familiar web APIs such as AngularJS.

For the full code, clone my github repository:
https://github.com/D...tarterTabsIntro

Note that you'll still have to do the ionic build and add platform steps.

Is This A Good Question/Topic? 2
  • +

Replies To: Hybrid Mobile Apps: Intro To AngularJS And Ionic Framework

#2 CasiOo  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1567
  • View blog
  • Posts: 3,520
  • Joined: 05-April 11

Posted 28 April 2015 - 10:02 AM

Is it true that the apps are running in a webview?
There might be performance limitations, that comes from using webviews
Consider the consequences webviews might have for your app. I believe iOS 7 even throttled webviews? I don't know why they intentionally would make their webviews slower.. probably to make Safari look that more amazing

We've had limited success with HTML5 web apps, and instead I'll advice Xamarin

I'm not against mobile web apps, but I do not like the idea of running it in a webview
Was This Post Helpful? 0
  • +
  • -

#3 techguydiy  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 10-May 15

Posted 10 May 2015 - 04:26 PM

I would say that there have been major steps forward in the support of web apps, but there is still a split second delay that simply isn't there on native development. If someone uses a lot of native apps, going to a web app can be a jarring experience. To your note about Unity, while it is true that you can build your game in javascript, it does export as a native app, not just as a webview.

I wouldn't say don't build apps as you are suggesting, I would just be very selective in the types of apps you build. These rapid development platforms are also great for prototyping. If you have an idea, build it out with this method and see what people think. It will cut down on a ton of overhead and get you to a "product" quicker. Having launched a few of these at large scale, I would have to say that the benefits in the short term, can be outweighed by the down sides as your app user base grows, especially on the Android platform.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1