In the last post we started looking into how to create our first application in Ionic by following along with the example in Ionic’s documentation. We saw how to build a simple Ionic application, but everything was laid out for us – we weren’t doing much more than copy and pasting. In order to start building our own applications we need to really understand how the technology works, and since Ionic is built on AngularJS we need to understand how _that _works.
I’ve been building the example application that will be used for the Mobile Development for Web Developers course (as I discussed with my email subscribers, I’ve decided to use Ionic instead of Sencha Touch for part of this course, partly for reasons outlined in the introduction post of this series).
The application is a fairly simple one, the idea is to recreate a photo a day style video. The app allows you to:
- Take a photo (preferably once a day)
- Store photos
- Delete unwanted photos
- Play a ‘video’ of all your photos in quick succession (which is more like a slideshow)
I’ve still got plenty to learn about Ionic, but I’ve learned a lot of the basics of Ionic and AngularJS through building this application. I’m quite surprised with the speed at which I’ve been able to build the application (it’s probably taken about 4 hours of work so far, plus some extra time researching, and it’s almost finished).
So, in this article I’m going to list the basic core concepts for AngularJS and Ionic I’ve learned as someone new to the frameworks. Before I get into that though I’ll summarise a few of the pro’s and con’s of Ionic vs Sencha Touch as I have seen them so far (since the last post).
Take these with a grain of salt, I’m still very much in the “honeymoon period” with Ionic – I have not had a chance to see many of Ionic’s flaws yet (any potential flaws I would likely chalk up to me just not knowing what I’m doing), and likewise things that I consider as con’s of Ionic may indeed just be that I don’t understand how to do it properly.
Pro’s:
- Very easy to start developing fast, especially if you’re already familiar with HTML5 mobile frameworks
- Ionic View is awesome for rapid prototyping and testing
- The Ionic CLI tool, specifically the ability to pull in examples from codepen.io when generating an application. Sencha Cmd is also a great tool but I think Ionic is just slightly better in this regard
- Extremely well documented with plenty of examples
- Integration with ngCordova makes using Cordova extremely fast and easy
Con’s
- The application structure doesn’t seem as well organised as Sencha Touch
- Storing data in local storage is more difficult than Sencha Touch’s approach
- The data system doesn’t seem to be structured as well as Sencha Touch’s
Again, these are all my interpretation at a first glance, don’t take them as gospel. Now… let’s learn some AngularJS!
NOTE: Some time in the future, likely early 2016, Angular 2 will be released which will include drastic changes to the way the framework works . This doesn't mean everything you learn now is useless, but you will likely need to relearn some things if you plan to switch to Angular 2. It'll be useful to understand both the "old way" and the "new way" anyway though, as it will give you a better understanding of why the changes were made and of course the skills to upgrade a Angular 1 app to Angular 2.
Ionic has stated that they will support Angular 2 with Ionic v2, as well as continuing to support Ionic v1.
The Ionic and AngularJS Crash Course
Angular is the biggest competitor for Sencha’s EXT JS framework. Essentially, they are both frameworks for building JavaScript applications. The following are the key concepts of Ionic and AngularJS which I have discovered as a newbie to the frameworks.
1. Expressions
The most obvious difference between Sencha Touch and Ionic / AngularJS is the mixture of JavaScript and HTML. In Sencha Touch, JavaScript and HTML is, more or less, kept completely separate. They are distance cousins who come together only when necessary. With AngularJS, HTML and JavaScript are best buddies.
You can execute JavaScript code within HTML by using expressions. An expression consists of some code wrapped in double curly braces, for example:
<span>2 + 2 = {{2+2}}</span>
This would output the following into the DOM:
<span>2 + 2 = 4</span>
2. Directives
A directive is basically like a little marker in your HTML that looks like this:
<some-directive></some-directive>
When your code is executed, it’ll be replaced by something more useful (like a list as I’m about to show you). A directive also allows you to define the structure of a page for a given set of data.
One example is ngRepeat which behaves like a for each loop. If you’re a Sencha Touch developer then you may notice some similarities in the way an ngRepeat directive works and defining a template for a list or dataview. Take the following code for example (we used this as part of the ToDo app tutorial in the last post):
<ion-list>
<ion-item ng-repeat = "task in tasks">
{{task.title}}
</ion-item>
</ion-list>
This creates a list item entry for every task, and displays the tasks title. This examples uses the <ion-list>
directive to create a scrollable list, and the <ion-item>
directive is used to create multiple items within that list. We could more simply just display the data by doing something like this:
<div ng-repeat = "task in tasks">
<h6>{{task.title}}</h6>
</div>
This would output the data as plain HTML instead of a list. Not nearly as exciting of course, but it may look something like this:
This is Task 1
This is another task
This is the third task
untitled
There are of course other directives besides ngRepeat, another is ngShow. You can use ngShow to determine if an element should be shown for a specific entry. For example if we didn’t want to show that untitled task we could use the following instead:
<div ng-repeat = "task in tasks" ng-show = "task.title != 'untitled'">
<h5>{{task.title}}</h5>
</div>
Note: Even though the element is not shown, it is still created in the DOM.
There’s a lot more to know about directives, so make sure to have a read through the directives page in the AngularJS documentation.
3. Data Binding
In AngularJS you can bind data by using the ngModel directive. Let’s use the example from the last section as an example:
<div ng-repeat = "task in tasks">
<h5>{{task.title}}</h5>
</div>
Let’s say we want to allow the user to edit that title live, we can add an input area that is bound to the title. When the input is updated then the title will be updated:
<div ng-repeat = "task in tasks">
<h5>{{task.title}}</h5>
<input type = "text" ng-model = "task.title" />
</div>
Now for each task that is listed there will also be an input box that can be used to change the title of that item.
4. Views
The structure of views and how to switch between them was something I struggled with for a while in Sencha Touch. As always, there’s different ways to implement navigation but it does seem quite simple to do in Ionic.
Just like a Sencha Touch application, Angular JS creates single page applications, where screen and content changes are handled by JavaScript rather than loading in a new page. In the index.html page we can bind part of the application to an AngularJS module by using the ngApp directive. Let’s see what that looks like in the ToDo application:
<body ng-app = "IonicTodo" ng-controller = "TodoController">
<!-- App Stuff -->
</body>
You can see that we have specified IonicTodo with the ng-app attribute (as well as a controller with the ng-controller attribute, but we’ll talk about that later). We also defined that module in our app.js file:
angular.module('IonicTodo', ['ionic'])
Note: The actual tutorial used slightly different names for some things
Now we can define a config on that module. I’m going off track from the tutorial here and this is a little more advanced (a more simple way to change views would be by using something like the ion-tabs directive) but we can include the $stateProvider and $urlRouterProvider to help control navigation between our views with Ionic. Here’s how the config might look:
angular
.module('ionicApp', ['ionic'])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('intro', {
url: '/',
templateUrl: 'templates/intro.html',
controller: 'IntroCtrl',
})
.state('main', {
url: '/main',
templateUrl: 'templates/main.html',
controller: 'MainCtrl',
});
$urlRouterProvider.otherwise('/');
});
We’re defining two states here, an intro state and a main state. In my photo application I am using a Slide Box with three slides for the intro view, and a normal content view with a list for the main view. The templates for these states are stored in the templates folder (the templates are just plain old HTML and Ionic / AngularJS directives like we’ve been using throughout this tutorial). Now in the index.html file I could have the following:
<body ng-app="ionicApp">
<ion-nav-view></ion-nav-view>
</body>
The directive is used to hold the views we want to switch between. To change to a different view (or “state”) you can then simply call:
$state.go('intro');
or
$state.go('main');
from one of your controllers (we will talk about controllers in the next section). For simple applications you may not even need to use states, as I mentioned before a simple tab view or side menu might be just fine. Keep it in mind though for when your applications get more complicated.
5. Controllers
Just like in Sencha Touch, controllers do a lot of the heavy lifting in Ionic. A controller in Ionic can be defined as follows:
.controller('IntroCtrl', function($scope, $state) {
// Called to navigate to the main app
$scope.startApp = function() {
$state.go('main');
};
$scope.someFunction = function(){
//do cool stuff here
};
})
Notice that we are including some parameters. These provide references to the things we require in the controller like the $scope and the $stateProvider we created in the previous section. To associate a part of the application with that controller you use the ng-controller attribute:
<body ng-app = "IonicTodo" ng-controller = "IntroCtrl">
<!-- App Stuff -->
</body>
Or, as you saw in Section 4 we defined the controller with the $stateProvider, in which case you wouldn’t need to specify the ng-controller attribute. Now if I was to call a function using the ng-click attribute on a button (a button that is in templates/intro.html if we are relying on the $stateProvider, or within the <body>
tags if we used the ng-controller attribute) like so:
<button
ng-click="someFunction()"
class="button button-icon icon ion-close"
></button>
The corresponding function in the controller would be called. You might be confused as to what $scope is, as it seems to be plastered everywhere around Ionic. Quite simply, it is a reference to the current scope of the ’this’ keyword. In Sencha Touch you might have done something like this to keep a consistent reference to the current scope:
var me = this;
me.doSomething();
The context of ’this’ can change, assigning it to a variable keeps it constant. You might be interested in reading more about this here.
6. Data Storage
The last core concept I want to cover is data storage. You can store data within your controller simply by creating an array:
$scope.photos = [];
Then you can add to that array by using the following code:
$scope.photos.push({
image: 'http://www.placehold.it/300x300',
});
Super simple JavaScript stuff. You probably want to save that data so the user can access it later though. That’s a little bit harder, and requires creating a ‘Factory’ which can help store and retrieve data (think of it like a Store in Sencha Touch). To create a factory, you can define it on your angular module like this:
angular
.module('ionicApp', ['ionic', 'ngCordova'])
.factory('Photos', function () {
return {
all: function () {
var photoString = window.localStorage['photos'];
if (photoString) {
return angular.fromJson(photoString);
}
return [];
},
save: function (photos) {
window.localStorage['photos'] = angular.toJson(photos);
},
newPhoto: function (image) {
return {
image: image,
};
},
};
});
Notice that we’re essentially just creating a way to fetch and retrieve data from the browsers local storage. Local storage can only store strings, so to store objects we need to first convert them to a JSON string, which we can then convert back into an array when we retrieve it. Before you can use this Factory within a controller, you must first make sure to pass it into the controller:
.controller('MainCtrl', function($scope, $state, Photos) {
});
Now I can retrieve all of the Photos from local storage by calling the following function:
$scope.photos = Photos.all();
and I can save new photos like this:
var createPhoto = function (image) {
var newPhoto = Photos.newPhoto(image);
$scope.photos.push(newPhoto);
Photos.save($scope.photos);
};
createPhoto('http://www.placehold.it/300x300');
Summary
That was a big one! I’ve mentioned a lot of new concepts so don’t worry if you feel a little overwhelmed. These are just the main things my brain marked as “hey, this seems important” and it certainly took me a little while to “get it” (I’m sure I still don’t understand all these things completely).
Hopefully this helps you get started, but there’s obviously a lot more that I haven’t included. So I highly encourage you to take a poke around the AngularJS documentation and the Ionic documentation. Read through some more concepts, look up things that look cool and try to create a few simple applications.
UPDATE: Part 4 is now available!