Dev Diary S01E03: Building the first Angular page

Introduction

In my previous post, Dev Diary S01E02, I explained the configuration of my environment for the client side. If you remember, I am building this application with Visual Studio Code and will also be building the server side component using Visual Studio 2015.

To be honest, I got impatient when developing this and decided that I would start to get to grips with the Angular part. I didn’t really understand things entirely but built my first page index.html.

 

Angular ng-app, ng-view and controllers

The Angular App Homepage (index.html)

So to get things running, I needed to reference the necessary components so that I could start to use them.

Angular is a JavaScript based Model View View Model based application framework which is used to build client side SPAs or Single Page Applications. We need a single page which is going to host the application. This is going to be our homepage, index.html.

Index.html had the following references added to load the Angular, jQuery, Bootstrap components. I added these under the closing tag as this allows the browser to load the page more quickly.


/bower_components/angular-route/angular-route.js
/bower_components/angular-resource/angular-resource.js
/bower_components/jquery/dist/jquery.js
/bower_components/bootstrap/dist/js/bootstrap.js

The other thing is that we need to tell Angular about the fact that this is an app and also tell it when to render the view.

Below is the index.html file

 

<!doctype html>
<html lang="en" ng-app="itspInvoiceFormApp">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>iThink SharePoint Invoice System</title>
<link rel="stylesheet" type="text/css" href="/bower_components/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/app/css/invoiceformapp.css" />
</head>
<body>
<div ng-view></div>
</body>
<script src="/bower_components/angular/angular.js"></script>
<script src="/bower_components/angular-route/angular-route.js"></script>
<script src="/bower_components/angular-resource/angular-resource.js"></script>
<script src="/bower_components/jquery/dist/jquery.js"></script>
<script src="/bower_components/bootstrap/dist/js/bootstrap.js"></script>
<script src="/app/controllers/invoiceControllers.js"></script>
<script src="/app/app.js"></script>
</html>
view raw index.html hosted with ❤ by GitHub

So you can see we have line #2 which an Angular directive, ng-app, which I will talk about a little later. This tells Angular that the application that will be loaded on this page is called itspInvoiceFormApp.

We have all the links to the scripts mentioned above and also the applications CSS for bootstrap.css and the application, invoiceformapp.css.

In the <body> tag, another Angular directive is added to a <div> tag. This is ng-view. This tells Angular where to render our view and provides the entry point for switching the content in our Single Page Application.

So now we have our index.html done lets move on to some JavaScript!

 

The Angular App Code (app.js)

Next, the Angular app was configured in /app/app.js. I had a look through the various examples on the Angular.js website and also out there via Bingle.

I added the following to app.js

 

'use strict';
var invoiceFormApp = angular.module('itspInvoiceFormApp',
[
'ngRoute', 'invoiceControllersModule'
]);
var appStart = function($routeProvider) {
$routeProvider.when('/invoices', {
templateUrl:'/app/views/list-invoices.html',
controller: 'listInvoicesController'
}).otherwise({
redirectTo: '/invoices'
});
};
invoiceFormApp.config(['$routeProvider', appStart]);
view raw app.js hosted with ❤ by GitHub

 

So lets talk through this, the first line #1 is telling JavaScript to run in strict mode. This helps check for errors in the JavaScript, more information can be found here:

Line 3, creates a variable called invoiceFormApp using the angular.module() function. We pass in the name of the module, in this case ‘itspInvoiceFormApp’. The next parameter in the square brackets are the dependencies that the application relies on. This is important, Angular has this dependency injection engine, which we can take advantage of. In this case we are dependent on the Angular routing module, called ngRoute.

Next, I had to to configure the application, so create a function which provides the configuration of the app. From my limited experience, I know that this is going to change quite a bit as we start loading in more dependencies.

So the function currently has one parameter, this will increase as the number of dependencies get added to the application. The configurationProvider function has one parameter which maps to the invoiceFormApp.config() which is passing in the objects in the [‘$routeProvider’] into the function. So if we had the following [‘$routeProvider’, ‘myOtherThing’], then we would update the function assigned to configurationProvider to:

var configurationProvider=function($routeProvider,$myOtherThing).

 

Angular Routing

Anyway, I hope that helps clear that function up a bit, it took me a bit of time to get the hang of it. So within my app, I used the $routeProvider to create a route for ‘/invoices’. Now, when you setup a route you need to provide a couple of additional bits of information.

  • templateUrl: ‘[server relative url to the html file that is going to hold the view’
  • controller: ‘[name of the controller that will handle setting up the view]’

I setup the /invoice route to go to a view located at ‘/app/views/list-invoices.html’ and to use a controller named ‘listInvoicesController’. Notice the final call to .otherwise() on the $routeprovider variable this will cause the application to default to this route and page if there is no match found for the user’s input or the user is directed by the application to an invalid endpoint..

Now we have our routes setup, I had to give them something to point to.

 

Views and Controllers(View Model)

With the default route created,  next I had to create a view and a controller to implement the route.

 

View

Lets start with the view first, I created a view in VS Code called “list-invoices.html” in the /app/views folder.

Finally, we are getting to try out Angular. This page is being used just to view the data.

This is what I ended up putting together for list-invoices.html.

 

<div class="container">
<div class="row">
<div ng-show="showInvoiceList" class="col-md-10 col-sd-3">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<th>Date</th>
<th>Invoice Reference</th>
<th>Company Name</th>
<th>Total</th>
<th>Status</th>
</thead>
<tbody>
<tr class="clickable-row" ng-repeat="invoice in invoices">
<td>{{invoice.invoiceDate}}</td>
<td>{{invoice.reference}}</td>
<td>{{invoice.companyName}}</td>
<td>{{invoice.currencyType}}{{invoice.invoiceTotal}}</td>
<td>{{invoice.status}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="row">
<div ng-show="error !== ''" class="col-sd-3">
Current Error: {{error}}
</div>
</div>
</div>

 

So basically I have a table wrapped up in some <divs> the outer <div> has the class name container, this is a bootstrap thing to demark the content that is to be styled by bootstrap, the inner div with the class name row creates a new row.

Now on to the actual Angular stuff. we have these various tags starting with “ng-“, these are called directives and allow you to do something with the data that you have bound to the page from your controller and data, or model.

The example that we can see is ng-show   (ine 5) this points to something in or model which evaluates to a boolean value and will display that <div> element when it evaluates to true.

I think this is pretty cool stuff, I was always amazed when I saw this kind of thing with Knockout.js

Anyway, so next we render our data, so we have a table which has been tagged with various bootstrap classes to make it responsive. At some point I am sure I will have to work out the responsive stuff works. The cool bit with the table, starts at row 16.

We have a line which reads <tr class=”clickable-row” ng-repeat=”invoice in invoices”>. So this Angular directive ng-repeat allows us to loop through an array of objects which are in an array called invoices.

This <tr> element block will get repeated for each invoice in invoices, within the <tr> we have a number of <td> elements which are being used to display the values in the properies found on the invoice  object. This is one way that Angular does its data-binding. So using {{invoice.invoiceDate}} we can display the contents within the invoice object.

The rest of the page is pretty uninteresting, I did also add a section to display errors which is quite neat and shows another thing that you can do with Angular directives which is actually add logic within the directive. So we have a <div> tag which has the ng-show  directive set to ng-show=”error!==’’”. So if there is an error in our data model then this div will show the error.

Anyway, so until I show you the controller, it does not really make sense as how do we know what data we are binding to the view?

 

Controller and Model

In order for the view to work we need to create a controller and also the model that the controller is going to pass into the view.

I created a file called invoiceControllers.js in the /app/controllers folder.

Within this file, I created a new module which created a module called InvoiceControllerModule. Then we registered a factory called invoiceController. I have to say that I now know that this was a mistake. But at the time I didn’t realise it. I will talk about the changes that I made in a later blog post.

Anyway, now that we have the factory we need to create a function which is called when the factory is initialised.

 

invoiceControllersModule.factory('listInvoicesController', ['$scope', function ($scope) {
$scope.invoices = [];
$scope.error="";
]

 

Note that the factory method takes a function and the parameters match the attributes defined in []. This controller is taking just a $scope variable.

Actually the $scope variable is very important and represents the view model which can be referenced within your (.html) view file.

We created a couple of properties of the $scope object to hold an error and also to hold a list of invoices.

Next, we need to add some invoices to the array, so that we have something to display.

To keep things easy I just created a load() method on the $scope object to load the data.

This now creates a controller which exposes a $scope variable, final file looks like this.

 

'use strict'
var invoiceControllersModule = angular.module('invoiceControllersModule', []);
invoiceControllersModule.controller('listInvoicesController', ['$scope', function ($scope) {
$scope.invoices = [];
$scope.error="";
$scope.showInvoiceList=function(){
return true;
};
$scope.load = function(){
var invoice=createInvoice();
invoice.reference="INV01";
invoice.companyName="Contoso";
invoice.agencyContact="Jack Black";
invoice.vatRate=20;
invoice.invoiceTotal=10000;
$scope.invoices.push(invoice);
invoice=createInvoice();
invoice.reference="INV02";
invoice.companyName="Fabrikam";
invoice.agencyContact="Jack Black";
invoice.vatRate=20;
invoice.invoiceTotal=10000;
$scope.invoices.push(invoice);
};
$scope.load();
function createInvoice(){
var invoiceObject={
reference: '',
companyName: '',
invoiceDate: '',
contact:'',
addressLine1:'',
addressLine2:'',
addressLine3:'',
addressLine4:'',
addressCity:'',
addressCounty:'',
addressPostCode:'',
addressCountry:'',
agencyName:'',
agencyContact: '',
invoiceLines: [
],
invoiceTotal:0,
currencyType:'£',
vatRate:0,
vatAmount:0,
invoiceTotalWithVat:0,
status:'',
createdBy:'',
modifiedBy:'',
created:'',
modified:'',
};
return invoiceObject;
};
}]);


'use strict'
var invoiceControllersModule = angular.module('invoiceControllersModule', []);
invoiceControllersModule.controller('listInvoicesController', ['$scope', function ($scope) {
$scope.invoices = [];
$scope.error="";
$scope.showInvoiceList=function(){
return true;
};
$scope.load = function(){
var invoice=createInvoice();
invoice.reference="INV01";
invoice.companyName="Contoso";
invoice.agencyContact="Jack Black";
invoice.vatRate=20;
invoice.invoiceTotal=10000;
$scope.invoices.push(invoice);
invoice=createInvoice();
invoice.reference="INV02";
invoice.companyName="Fabrikam";
invoice.agencyContact="Jack Black";
invoice.vatRate=20;
invoice.invoiceTotal=10000;
$scope.invoices.push(invoice);
};
$scope.load();
function createInvoice(){
var invoiceObject={
reference: '',
companyName: '',
invoiceDate: '',
contact:'',
addressLine1:'',
addressLine2:'',
addressLine3:'',
addressLine4:'',
addressCity:'',
addressCounty:'',
addressPostCode:'',
addressCountry:'',
agencyName:'',
agencyContact: '',
invoiceLines: [
],
invoiceTotal:0,
currencyType:'£',
vatRate:0,
vatAmount:0,
invoiceTotalWithVat:0,
status:'',
createdBy:'',
modifiedBy:'',
created:'',
modified:'',
};
return invoiceObject;
};
}]);

Now, that this is all put together it generates a page which looks a little like this.

image

 

Make sure that if you are loading some data n your controller with a load() function, that you call it. I spent a couple of minutes, wondering why I did not have any data loaded and the realised I hadn’t actually called the function.

 

When testing and debugging use http://localhost over http://127.0.0.1

Something that I noticed with Google Chrome when testing the environment is that the css would not work correctly if I tested using http://127.0.0.1:8080 so I ended up using http://localhost:8080 which caused the styling to render correctly.

 

Next Episode

I hope you found this useful, please let me know if there is something that needs more clarity.

I have moved this code into GitHub and it is currently available here:

https://github.com/ithinksharepoint/IT365.InvoiceFormApp.Client/tree/master

In the next episode we are going to put together some add functionality and also configure the application navigation.

Thoughts? Comments about this post? Please leave them here..

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.