findcommonground.uk/_posts/2016-03-15-Reisekosten-App-Frontend.md

258 lines
14 KiB
Markdown
Raw Normal View History

2016-03-15 13:09:52 +00:00
---
layout: post
title: Extending On-Premise Products With Mobile Apps - Follow-up
subtitle: Creating a Single Page App using Apache Cordova and AngularJS
category: general
tags: [mobile, cloud]
author: Biro Carol
author_email: carol.biro@haufe-lexware.com
header-img: "images/bg-post.jpg"
---
### What is this about?
2016-03-17 08:58:53 +00:00
This is a follow-up blog-post about a proof-of-concept project I have worked on together with my colleague Robert Fitch to find out what it takes to access on-premise data from an internet client. Robert have created the server side API and my role was to create a mobile app which consumes the methods exposed by this API. The technologies used in order to create the app were HTML5, AngularJS, Bootstrap css and Apache Cordova.
2016-03-15 13:09:52 +00:00
### Why Apache Cordova?
2016-03-17 08:58:53 +00:00
Apache Cordova targets multiple platforms with one code base, it is open source and for the apps you can use html, css and javascript. There are many pros and cons using this stack of technology but in this case we needed to address the app to a wide range of users with as little effort as possible. That is why we didn't use a pure native approach and for the proof of concept app we have targeted Android and iOS devices as potential consumers.
### What else do we need?
Apache Cordova is offering a good way to organize your project. It assured OS specific customizations and the OS specific mobile app builds. These being set up once are for good or very little modified during the lifetime of the project. For the effective development we have used AngularJS and Bootstrap.css.
Maybe it is interesting to know that when I have started to work on this project I had no experience with the above mentioned technologies: neither Apache Cordova , nor AngularJS or Bootstrap.css. I am a pretty experienced web developer who have worked before mainly on jquery based projects. Starting to learn about AngularJS I have discovered a new way of thinking about web development, that is how to develop a web application avoiding to use jquery , use two way bindings etc.
Beside frontend development I mainly do my daily job using Microsoft technologies like C#. I do this using Visual Studio as IDE. That is why a good choice to set up this project was to use Visual Studio Tools for Apache Cordova. By the time I have started to work on the project the Update 2 of these tools were available, now after a couple of months Update 7 can be downloaded with a lot of improvements.
{:.center}
2016-03-17 09:06:40 +00:00
![Reisekosten App Frontend - Visual Studio Tools for Apache Cordova]( /images/reisekosten-app/visualstudioupdate7.jpg){:style="margin:auto"}
2016-03-17 14:30:05 +00:00
This being installed you have what you need to start the work. It installs Android SDK and a lot of other dependent stuff you might need when you start working. I will not detail this since this has been done before by others. If you want to read about you have a lot of resources available like :
http://taco.visualstudio.com/en-us/docs/get-started-first-mobile-app/
### Front-End requirements
In a few words we needed:
- a login page
- a list of trips
- the possibility to create/edit a trip
- add/edit receipts assigned to a trip
- the receipts form needed visibility rules of some fields(depending on a category)
### The project
Knowing the above here is a screenshot of how I ended up structuring the project:
{:.center}
![Reisekosten App Frontend - Visual Studio Tools for Apache Cordova]( /images/reisekosten-app/projectstructure.jpg){:style="margin:auto"}
In the upper part of the solution we can see some folders where OS specific things are kept. For instance merges folder has android and ios subfolders each of them containing a css folder where resides a file called overrides.css. Of course these files have different content depending on the OS. What is cool with this is that at build time this Visual Studio project knows where to place automatically these files so that specific overrides actually happen in the apps.
The plugins folder is cool too , here will reside the plugins which helps to extend the web application with native app abilities. For instance we can install here a plugin which will be able to access the device camera.
The res folder contains OS specific icons (e.g. rounded icons for IOS and square icons for Android) and screens. Finally in the upper part of the soulution there is a test folder where the unit and integration tests will reside.
The next folder www contains the project itself, the common code base. We see here a bunch of files which for me seems nicely and clearly organized, maybe not for you yet, but hopefully things become more clear with the next screenshot of the index.html where the whole app is running:
```HTML5
<!doctype html>
<html ng-app="travelExpensesApp" ng-csp>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="email=no">
<meta name="format-detection" content="telephone=no">
<link href="css/bootstrap.css" rel="stylesheet">
<link href="css/bootstrap-additions.css" rel="stylesheet" />
<link href="css/index.css" rel="stylesheet"/>
<!-- Cordova reference, this is added to the app when it's built. -->
<link href="css/overrides.css" rel="stylesheet"/>
<!-- Angular JS -->
<script src="scripts/frameworks/angular.js"></script>
<script src="scripts/frameworks/angular-resource.js"></script>
<script src="scripts/frameworks/angular-route.js"></script>
<script src="scripts/frameworks/angular-strap.js"></script>
<script src="scripts/frameworks/angular-strap.tpl.js"></script>
<script src="scripts/frameworks/angular-input-masks-standalone.min.js"></script>
<!-- Cordova reference, this is added to the app when it's built. -->
<script src="cordova.js"></script>
<script src="scripts/platformOverrides.js"></script>
<!-- Initialize all the modules -->
<script src="scripts/index.js"></script>
<!-- Services -->
<script src="scripts/services/cordova.js"></script>
<script src="scripts/services/global.js"></script>
<script src="scripts/services/httpInterceptor.js"></script>
<!-- Controllers -->
<script src="scripts/controllers/loginController.js"></script>
<script src="scripts/controllers/tripsController.js"></script>
<script src="scripts/controllers/tripDetailsController.js"></script>
<script src="scripts/controllers/receiptsController.js"></script>
<script src="scripts/controllers/receiptDetailsController.js"></script>
</head>
<body >
<div ng-view></div>
</body>
</html>
```
Being a SPA everything runs in one place. All the necessary files are included in the header : first the app specific css files, then the css override file which is replaced with OS specific one at build time. Next are included the AngularJS and AnfularJS specific libraries and then comes the platform specific javascript overrides. Until now we have included only libraries and overrides, what comes from now is the base of the app the index.js.
A code snippet from here helps to understand the AngularJS application:
```javascript
(function () {
"use strict";
var travelExpensesApp = angular.module("travelExpensesApp", ["ngRoute", "mgcrea.ngStrap", "ui.utils.masks", "travelExpensesControllers", "travelExpensesApp.services"]);
angular.module("travelExpensesControllers", []);
angular.module("travelExpensesApp.services", ["ngResource"]);
travelExpensesApp.config([
"$routeProvider",
function($routeProvider) {
$routeProvider.
when("/", {
templateUrl: "partials/login.html",
controller: "LoginControl"
}).
when("/companies/:companyId/employees/:employeeId/trips", {
templateUrl: "partials/trips.html",
controller: "TripsControl"
}).
when("/companies/:companyId/employees/:employeeId/trips/:tripId", {
templateUrl: "partials/tripDetails.html",
controller: "TripDetailsControl"
}).
when("/companies/:companyId/employees/:employeeId/trips/:tripId/receipts/:receiptId", {
templateUrl: "partials/receiptDetails.html",
controller: "ReceiptDetailsControl"
}).
otherwise({
redirectTo: "/"
});
}
]);
```
The AngularJS application is organized using modules. We can think of modules as containers for controllers , services, directives, etc. These modules are reusable and testable. What we can see above is that I have declared an application level module (travelExpensesApp) which depends on other modules .
I have created a separate module for controllers(travelExpensesControllers) and one for services(travelExpensesApp.services). I have also used some libraries as modules like : ngRoute (from angular-route.js), mgCrea.ngStrap (from angular-strap.js) and ui.utils.masks (from angular-input-masks-standalone.min.js). The module declarations links everything together and creates the base of the app.
Beside modules the above screenshot contains the configuration of routing. Using ngRoute lets us use routing which helps us a lot to define a nice and clear structure for the frontend app. AngularJS routing permits a good separation of concerns. GUI resides in html templates defined by the templateUrls and the logic behind is separated in the controller javascript files.
2016-03-17 14:51:12 +00:00
To understand a bit better let's take a look on the trips page which is built up from trips.html as template and TripsControl as controller:
```HTML5
<div class="navbar navbar-inverse">
<div class="navbar-header pull-left">
<button type="button" class="btn-lx-hamburger navbar-toggle pull-left" data-toggle="collapse" data-target="#myNavbar" bs-aside="aside" data-template-url="partials/aside.html" data-placement="left" data-animation="am-slide-left" data-container="body">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand padding-rl10">
<span class="glyphicon glyphicon-user"></span> {{employeeData.first_name}} {{employeeData.last_name}}
</a>
</div>
<div class="navbar-header pull-right">
<a class="navbar-brand" ng-disabled="true" ng-click="newTravel()"><span class="glyphicon glyphicon-plus"></span></a>
</div>
</div>
<div class="container">
<div waithttp ng-show="!waitingHttp">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Datum</th>
<th>Von</th>
<th>Nach</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="trip in trips | orderBy : departure_date : reverse" ng-click="editTravel(trip.id)">
<td>
{{ trip.departure_date | date : 'dd.MM.yyyy'}}
</td>
<td>
{{ trip.departure }}
</td>
<td>
{{ trip.destination }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
```
The above html contains a navbar and a simple list of trips. What we notice from the start are the double bracelets (e.g. '{{ trip.departure }}') and directives starting with "ng" prefix. These are the main ways how the template interacts with the controller :
```javascript
(function () {
"use strict";
angular.module("travelExpensesControllers").controller("TripsControl", ["$scope", "$http", "$routeParams", "$location","global", TripsControl]);
function TripsControl($scope, $http, $routeParams, $location, global) {
$scope.companyId = $routeParams.companyId;
$scope.employeeId = $routeParams.employeeId;
$scope.employeeData = global.user.employeeData;
$scope.aside = {
"title" : global.user.employeeData.first_name + " " + global.user.employeeData.last_name
};
var tripsRequest = {
method : "GET",
url : global.baseUrl + "companies/" + $scope.companyId + "/employees/" + $scope.employeeId + "/trips",
headers : {
"Content-Type": "application/hal+json",
"Session-Id": global.sessionId
}
};
$http(tripsRequest).then(
function (data) {
$scope.trips = data.data.ResourceList;
},
function (data) {
$scope.error = data;
// session probably bad, go back to login page
$location.path("/");
});
$scope.newTravel = function () {
delete global.tripDetails;
var url = "/companies/" + global.user.companyData.id + "/employees/" + global.user.employeeData.id + "/trips/0";
$location.url(url);
}
$scope.editTravel = function (travelId) {
var url = "/companies/" + global.user.companyData.id + "/employees/" + global.user.employeeData.id + "/trips/" + travelId;
$location.url(url);
}
$scope.doLogout = function () {
$location.path("/").search({invalidate: true});
}
}
})();
```
Above we can see how the TripsControl is defined . To expose something usable in the template we just exetend/add a new property or function to the $scope variable. For example we get the list of trips if we have a successfull answer for a $http request defined by js var tripsRequest.
In a nutshell this is how angular works and used in this POC.
### What else ?
Since the most of the time during this POC I have worked with AngularJS you might wondering if I had to implement something else which is worth mentioning.