News

Build a hybrid app with the Ionic framework

Ever wanted to develop a native app but have been put off by learning a new language and ecosystem? Enter the Ionic framework, which lets you apply JavaScript knowledge to build hybrid apps.

IconicHybridApp

Ever wanted to develop a native app but have been put off by learning a new language and ecosystem? Enter the Ionic framework, which lets you apply JavaScript knowledge to build hybrid apps.

IconicHybridApp


GET THE CODE FOR THIS TUTORIAL

Ionic offers other services to build APIs, a WYSIWYG editor, push notification handling, analytics and more. We’ll be concentrating on building with the core framework. Ionic is fast but still not as performant as building natively; this makes it well suited for prototyping or delivering an MVP.

Ionic was released in 2013 by a company called Drifty. Since then it has seen increasing adoption due to its ease of use and modern toolchain. It also looks gorgeous out the box and isn’t opinionated about the editor you use like some similar mobile frameworks. Ionic is powered by Angular so we’ll assume basic working knowledge of Angular so we can focus on what Ionic brings to the table.

To test Ionic out you’re going to build a simple app which will list recipes from the Food2Fork API. You’ll also be able to select your favourites, which will be displayed in a separate view. You’ll learn the basics to get Ionic running and we’ll touch upon some of the more advanced features it exposes to take these skills further.

1. Install Ionic’s dependencies

Ionic uses a modern workflow so you’ll need Node.js 4 installed first. If you’re on Windows the Visual Studio Community IDE includes everything that you will need to get started. If you are working on a Mac then you’ll need to install Xcode from the App Store. For Android development you can go to bit.ly/1Tt3tDF.

2. Scaffold the project

Through the command-line we use Ionic to create a new project called ‘guthub’, which adds all the necessary files and folders for an app. We then add iOS as a build target and run it. This kicks off building the application and running it in the iOS Simulator on OS X.

$ ionic start guthub
$ cd guthub
$ ionic platform add ios
$ ionic run

3. Script dependencies

In index.html add the script references for our JavaScript files. Only app.js exists currently so you’ll get some 404 errors but it saves us from having to keep adding files later on. Just like a normal site, index.html is the starting point and includes all the setup needed. Change ng-app on body to guthub.

<script src="js/app.js"></script>
<script src="js/controllers/list.js"></script>
<script src="js/controllers/favourites.js"></script>
<script src="js/controllers/recipe-detail.js"></script>
<script src="js/services/recipes.js"></script>
<script src="js/services/favourites.js"></script>
<script src="js/services/localstorage.js"></script>

4. Create the recipe service

First off we want some recipes to display. Create a services folder and a file called ‘recipes.js’. You can grab the recipe array from bit.ly/1KMl4Pz and paste it in. We’ll mimic an API by exposing a simple get function. You’ll also need to add ngResource as a script dependency.

<script src="lib/ionic/js/angular/angular-resource.min.js"></script>
angular.module('guthub.services', ['ngResource', 'ionic.utils'])
.factory('Recipes', function ($resource) {
return {
get: function (id) {
// next step
}
};
});

5. Get recipe function

If the get function is passed an ID then it will loop through the recipes until it finds a match and return it. If nothing is found then null is returned. If no ID is passed as an argument then it will return all of the recipes.

if (id) {
for (var i = 0; i < recipes.length; i++) {
if (recipes[i].recipe_id == id) {
return recipes[i];
}
}
return null;
} else {
return recipes;
}

6. Add tabs to the view

In the templates folder open tabs.html. We’re going to tweak tabs.html slightly to show two tabs with different icons. Icon-off is the icon to show when the tab isn’t selected, and on is for when it is. Note that we’re sending it to a route defined in app.js. The nav-view name will link to the template.

<ion-tabs class="tabs-icon-top tabs-color-active-positive">
<ion-tab title="Recipes" icon-off="ion-ios-list-outline" icon-on="ion-ios-list" href="#/tab/recipes">
<ion-nav-view name="tab-recipes"></ion-nav-view>
</ion-tab>
<ion-tab title="Favourites" icon-off="ion-ios-star-outline" icon-on="ion-ios-star" href="#/tab/favourites">
<ion-nav-view name="tab-favourites"></ion-nav-view>
</ion-tab>
</ion-tabs>

7. Create recipe list view

Create a new template file called ‘tab-recipes.html’. Each of these custom tags are Angular directives that are documented at ionicframework.com/docs/api/directive. A view creates a new header/footer, content creates a content area with customisable scrolling and the list creates a list.

<ion-view view-title="Recipes">
<ion-content>
<ion-list>
<!— next step —>
</ion-list>
</ion-content>
</ion-view>

8. Add items to the list

To add items to this list use the ion-item directive. Some built-in Ionic classes are used to effectively style the list. Using ng-repeat we’ll easily repeat this markup for each recipe in the array.

<ion-item class="item-remove-animate item-avatar item-icon-right" ng-repeat="recipe in recipes" type="item-text-wrap" href="#/tab/recipes/{{recipe.recipe_id}}">
<img ng-src="{{recipe.image_url}}">
<h2>{{recipe.title}}</h2>
<i class="icon ion-chevron-right icon-accessory"></i>
</ion-item>

9. Create the Recipes controller

In app.js, update the route name and controller to match. Then create a folder called ‘controllers’ and within it a file called ‘recipes-list.js’. We’ll inject the Recipes service from earlier and use the get method to fetch all the recipes. It’s assigned to the scope so they’ll be exposed to the view.

angular.module('guthub.controllers', ['guthub.services'])
controller('RecipesCtrl', function ($scope, Recipes) {
$scope.recipes = Recipes.get();
});

10. Create recipe detail view

Now we will create the view that actually lists the data about the recipe and the ability to add it to our favourites. Note that we can dynamically set the view’s title using an Angular expression. Ionic includes some utility classes for common tasks like adding padding.

<ion-view view-title="{{recipe.title}}">
<ion-content class="padding">
<img class="recipe-image" ng-src="{{recipe.image_url}}">
<!— next step —>
</ion-content>
</ion-view>

11. Build the Favourite buttons

You still can’t favourite the recipe that you want, so let’s add in a couple of buttons. Again, Ionic includes the button classes and this will perfectly style it on every platform. On click we’ll now either add or remove the recipe. It’ll then toggle the button that has been shown with ng-show.

<button class="button icon-left ion-star button-positive" ng-click="addFavourite(recipe)" ng-show="!recipe.addedToFavourites">Add</button>
<button class="button icon-left ion-star button-negative" ng-click="removeFavourite(recipe)" ng-show="recipe.addedToFavourites">Remove</button>

12. The recipe detail controller

When you navigate to a URL we can add state parameters to pass specific data through. We’ll use this to get the ID of the recipe we’re looking for and add that to the scope. If it’s already in the favourites then we’ll add the flag to show this.

angular.module('guthub.controllers')
.controller('RecipeDetailCtrl', function ($scope, $stateParams, Recipes, Favourites) {
$scope.recipe = Recipes.get($stateParams.recipeId);
if (Favourites.get($scope.recipe.recipe_id)) {
$scope.recipe.addedToFavourites = true;
}
});

13. Add/remove favourites

To add the favourite we call Favourites.add. This won’t work yet as we need to write the Favourites service. To remove the recipe from your favourites we do the opposite. We could just expose the service to the view, but this gives us more granular control.

$scope.addFavourite = function () {
Favourites.add($scope.recipe);
};
$scope.removeFavourite = function () {
Favourites.remove($scope.recipe);
};

14. Ionic formulas

Ionic has a number of formulas, we’ll use the localStorage formula (see learn.ionicframework.com/formulas/localstorage). This could even be easily extended to use iCloud Backup to persist data across devices.

angular.module('ionic.utils', [])
factory('$localstorage', ['$window', function($window) {
return {
// next step
};
}]);

15. Set and get storage

The local storage service is just an abstraction for setting and getting named local storage strings.

set: function (key, value) {
$window.localStorage[key] = value;
},
get: function (key, defaultValue) {
return $window.localStorage[key] || defaultValue;
}

16. Create the favourites service

The favourites service adds, sets, removes and finds favourites. It tries to find stored favourites in local storage. If none are found then this is an empty array.

angular.module('guthub.services')
.factory('Favourites', function ($localstorage) {
var stored = $localstorage.get('favourites');
var favourites = stored ? JSON.parse(stored) : [];
});

17. Add/remove favourites

The add function takes a recipe, sets the addedToFavourites boolean to true and pushes that to the favourites array. Use the local storage service to set the favourites string by stringifying the array; and this preserves the content of objects.

return {
add: function (recipe) {
recipe.addedToFavourites = true;
favourites.push(recipe);
$localstorage.set('favourites', JSON.stringify(favourites));
return recipe;
},
};

18. Remove from favourites

The remove method is similar to the add method, the exception is that it splices the recipe from the array. It uses indexOf to find where in the recipe the matching recipe is. Splice is a ‘destructive’ method in that it changes the original array.

remove: function (recipe) {
favourites.splice(favourites.indexOf(recipe), 1);
recipe.addedToFavourites = false;
$localstorage.set('favourites', JSON.stringify(favourites));
return recipe;
}

19. The Favourites controller

The controller for favourites will simply get the Favourites (using a very similar method to fetching recipes). From this view we’ll use a special button to remove from the favourites array directly from the list. Remember to update app.js with the Favourites controller!

angular.module('guthub.controllers')
controller('FavouritesCtrl', function ($scope, Favourites) {
$scope.favourites = Favourites.get();
$scope.removeFavourite = function (recipe) {
Favourites.remove(recipe);
};
});

20. Listing favourites

To list favourites we’ll use a similar view to listing recipes for consistency. Note the use of ng-src instead of simply setting the src attribute. This prevents requests to {{recipe.image_url}} which would otherwise 404.

<ion-view view-title="Your favourites">
<ion-content>
<ion-list>
<ion-item class="item-remove-animate item-avatar item-icon-right" ng-repeat="recipe in favourites" type="item-text-wrap" href="#/tab/recipes/{{recipe.recipe_id}}">
<img ng-src="{{recipe.image_url}}">
<h2>{{recipe.title}}</h2>
</ion-item>
</ion-list>
</ion-content>
</ion-view>

21. Remove from the list

Ionic allows for some advanced gestures really easily. The ion-option-button directive will reveal a button when you slide to the left on an ion-item. We’ll use this to create a shortcut to the remove favourite action.

<i class="icon ion-chevron-right icon-accessory"></i>
<ion-option-button class="button-assertive" ng-click="removeFavourite(recipe)">
Remove
</ion-option-button>

GET THE LATEST ISSUE OF WEB DESIGNER NOW


×