#100DaysOfCode Days 34-37: Making Your Ionic App Work with Controllers and Services

The introduction is usually the part where I apologize for not updating you all daily on my progress. Sticking to a no excuses response, I’ll leave a link to my GitHub repo here for you all to follow the #100DaysOfCode challenge in real-time.  The best way for me to remedy being behind on my writing is not be. Therefore, let’s continue and talk about controllers and services.

If you use AngularJS, you are at an advantage.  Being based on AngularJS, Ionic makes the conversion from web app to mobile hybrid pretty seamless with a few tweaks.  Those unfamiliar with AngularJS can get familiar with it going through the documentation, but you also should be proficient in JavaScript when writing the functionality of an Ionic app.   JavaScript concepts, such as the use of objects, lists, closures, DOM manipulation, and templating are used heavily in Angular.  For this reason, I provided some resources below to better understand how to write better AngularJS controllers.

Also, please note that this post is a continuation of my series on building hybrid mobile apps with Ionic specifically. I’ve been walking through an app that I built for a course as part of a full stack web development certificate I’m earning this Spring through Hong Kong University via Coursera.  To get a series overview, you can go to the #100DaysOfCode page of the blog and read Days 26-33.

Linking JS Files in Your Ionic App

The index.html file of my app is already populated because I ran the ionic start appname sidemenu command upon installation of Ionic, thus also installing the sidemenu template and the views that come with it.  Using a template will save you trouble of having to write up the index.html file yourself and making sure on Ionic dependencies are linked beforehand.  Here’s a snapshot of my index.html file:

my index.html file

my index.html file

As you can see, it’s within the index.html file where the libraries and scripts are linked.  Pay attention to the bottom.  I linked three JS files, one for my app configuration, one for the controllers of the app, and one for the services.  I also use a ngDirective to name the app in the <body> tag and <ion-nav-view> to make sure the views are being fed from the www/templates subfolder of the app.

Initializing Your App

In my app.js file, I create a global variable with the Angular module. The parameters of the module argument is my app (the app name) and an empty array, which will take all the app dependencies. Under the module you have the .run() function that is included with the template used, and app states used for routing.  I discussed the app.js file more in detail in the previous post on app states you may review here so there’s no need to repeat myself today, it’s just important to note how a module is declared.  The following line of code is what my Angular module looks like now for the completed app:

angular.module('conFusion', ['ionic', 'ngCordova', 'conFusion.controllers', 'conFusion.services'])

The controllers.js file is where all the code functionality for the app is located.  I initialized the module for controllers with this line of code:

angular.module('conFusion.controllers', [])

Underneath the module includes each controller used in the app and the code functionality.

Setting Up Controllers and Services

Because I’m technically not allowed to share the complete project code for the course, I will share only a snippet as an example of controller setup.  Will look at the IndexController which is connected to the index.html view. My controller for the index.html does a couple of things:

  1. Defines the service $scope as the core dependency. Remember, a dependency is when an object is passed to a function instead of creating that object outside the function. For my IndexController, dependencies injected included both objects as well as services and factories I defined in the services.js file.
  2. Takes other dependencies within it’s array that are data from my db.json file, such as the dish, leader, and promotion objects.
  3. Takes the menuFactory, promotionFactory, and corporateFactory as dependencies as well. Factories are not to be confused with services, which I will go into later. For now, just know that a factory is a function in the services.js file that returns a value needed in the controller.
  4. Takes baseURL as  a dependency. baseURL is a parameter in the .constant() function defined in the services.js file which directs the user to the local url address to be used to view.  This may be the local host server address in the beginning, and later changed to your IP.
  5. Defines the function to be used with all of the above as dependencies as well.  Yes, you have to include all dependencies for the controller array within your function. It may seem recursive, but without doing so, you will get reference errors.
  6. Within the function, objects are assigned as properties of the $scope to initiate two-way binding with the view.

Here’s what my IndexController is written as:

.controller('IndexController', ['$scope', 'dish', 'leader', 'promotion', 'menuFactory', 'promotionFactory', 'corporateFactory', 'baseURL', function($scope, dish, leader, promotion, menuFactory, promotionFactory, corporateFactory, baseURL) { 
    $scope.baseURL = baseURL;
    $scope.dish = dish;
    $scope.leader = leader;
    $scope.promotion = promotion;

Let me break down each property and why is set to $scope.  As I said earlier, dish, leader, and promotion is data from the db.json file I created.  Without sharing the data from that file, know that each property stands for an individual id within the database for the dishes, leadership, and promotions objects.  The controllers get the id for each dish, leader, and promotion using factories that are functions that return those values.

Using Factories and Services to Return Values

Switching to my services.js file, the module for services is initialized, only unlike the controllers module, the services module takes ngResource within the array. Angular’s ngResource is a service that creates a resource object for interacting with data sources (like my db.json file). The module is set with one line of code as the global variable:

angular.module('conFusion.services', ['ngResource'])

Underneath, are the factories and services used for the act.  Here are the code snippets of the menuFactory, promotionsFactory, and corporateFactory from my services.js file that were injected as dependencies in the IndexController. Each factory takes the $resource  and baseURL services from the app.js file as a dependencies, and defines a function that returns the id from objects in the db.json file that is to be used in the controller as well as displayed with expressions in the template view using the PUT method:


factories within the service.js file

By assigning the properties of the scope in IndexController, I set up two-way binding with the view expressions for index.html file, which my app state routes to the home.html template within the www/templates project subfolder. For example, take the following code from home.html which displays the leader data using expressions:

Ionic cards from the home.html template

Ionic cards from the home.html template

Now let’s review what was discussed in short summary:

  1. Controllers control the code seen in the template view (any html client-side file) via two-way binding. For each controller, dependencies are injected.
  2. Dependencies are any objects, services, or factories that the controller depends on for the function. They are injected in the controller and as parameters for the function argument. The example of the IndexController used all three as dependencies.
  3. In the controller function, dependencies can be assigned to the $scope as properties. The dish, leader, and promotion objects were assigned as properties to $scope in the example.
  4. The controller was able to use these objects by injecting the menuFactory, promotionFactory, and corporateFactory from the services.js file.
  5. Factories are functions that return a value.  All three factories returned data from an individual id within the db.json file.
  6. Factories are able to retrieve data from the db.json file using the ngResource (or $resource) service as a dependency injected in the services module for the app.
  7. The template view displays values from the controllers using Angular expressions. They are defined within Ionic templates using curly brackets.

All together, app controllers, services, factories, templates, and database objects work together to provide a fully functioning app powered using JavaScript.

In the next and last post of this series, I will go over deploying an Ionic app for Android and iOS.

*** Resources for Further Learning ***

  1. Go through Thinkster’s guide, A Better Way to Learn AngularJS here. It discusses controllers in detail and provides a curated list of learning modules to start with.
  2. Tony Alicea is a great web developer and even more awesome course instructor. If you ever taken his course, JavaScript: The Weird Parts, you know how well he can explain even the most complex JavaScript concepts. Thankfully for us, he has released his new series on AngularJS. You can take the first hour of his AngularJS course on YouTube here.

*** Days 34-37 Recap ***

  1. Finished week 4 lectures of the Web Design course, started on final assignment
  2. Worked on photo_gallery project, finished it
  3. Finished week 2 of the Mobile App Dev course lectures, submitted assignment. Passed at 100%.
  4. Finished week 3 of the Mobile App Dev course lectures