Wednesday, April 27, 2016

AngularJS Core Concepts

AngularJS is an Open Source Java Script Framework built by Miško Hevery and Adam Abrons in 2009.


Benefits:
- Easy to use
- Dependency Injection
- 2-ways Data Bindings
- Clear MVC : separate model, view and controller.
- Easy Testability
- Useful features such as directives, filters, etc..

Start to use:
Download Angluar.js file or angular.min.js (minified and obfuscated version to improve the page loading speed) and import it in your view (HTML) file.
Download from : https://angularjs.org/
Angular Modules:
Angular is designed based on modules which works as a container for all other angularJS components.
You can define any module using:
angular.module("module_name",[list_of_dependency_modules]
e.g. var myApp = angular.module("myApp",[]);
We can use this module inside our view or create anonymous application using ng-app
Angular controller:
Is a java script function that get executed in a specific scope.
For example we can add to our app a controller using:
var myController = function($scope){
    $scope.employees=[
        { "firstName" : "Osama", "lastName": "Oransa","picture": "/images/osama.png", "hiredate": new Date("June 29, 2000"), "salary": 1000, "tax": 0},
        { "firstName" : "Ahmed", "lastName": "Samir","picture": "/images/ahmed.png", "hiredate": new Date("November 06, 2000"), "salary": 1500, "tax": 0},
        { "firstName" : "Sameh", "lastName": "Alaa","picture": "/images/sameh.png", "hiredate": new Date("October 19, 2000"), "salary": 2000, "tax": 0},
        { "firstName" : "Toka", "lastName": "Ibrhaim","picture": "/images/toka.png", "hiredate": new Date("May 21, 2000"), "salary": 1400, "tax": 0}
        ];
}
myApp.controller("myController",myController);
So these 2 steps we have create a controller and added it to our application/module.
We can do this in a single step using:
myApp.controller("myController",function($scope){
    $scope.employees=[
        { "firstName" : "Osama", "lastName": "Oransa","picture": "/images/osama.png", "hiredate": new Date("June 29, 2000"), "salary": 1000, "tax": 0},
        { "firstName" : "Ahmed", "lastName": "Samir","picture": "/images/ahmed.png", "hiredate": new Date("November 06, 2000"), "salary": 1500, "tax": 0},
        { "firstName" : "Sameh", "lastName": "Alaa","picture": "/images/sameh.png", "hiredate": new Date("October 19, 2000"), "salary": 2000, "tax": 0},
        { "firstName" : "Toka", "lastName": "Ibrhaim","picture": "/images/toka.png", "hiredate": new Date("May 21, 2000"), "salary": 1400, "tax": 0}
        ];
});
$scope object is available to set data models and behavior so we can use them inside the view.
We can also delete from the scope object:
delete $scope.employees;

Defining behavior:
$scope.function_name = function(parameters) {
   ... code here
}
For example:
$scope.calculateTax= function(employee){
    employee.tax= employee.salary *0.2;
}
This add behavior and data model to our scope object where we can now use it from inside the view :
Example:
<html ng-app="myApp">
<head>
<script src="angular.js"/>
</head>
<body>
<div ng-controller="myController">
  <table>
      <tr ng-repeat="emp in employees">
        <td> Photo : <img ng-src="{{ emp.picture}}" />  </td>
        <td> First Name : {{ emp.firstName}}  </td>
        <td> Last Name : {{ emp.lastName}}  </td>
        <td> Hiredate : {{ emp.hiredate }}</td>
        <td> Salary : {{ emp.salary}}  </td>
        <td> Tax : {{ emp.tax}} <input type="button" value="Tax" ng-click="calculateTax(emp)"/> </td>
    </tr>
    </table>
</div>
</body>
</html>
We can have nested controllers and each one will be limited to its scope.
Directives, Events and Bindings:
In the previous example we have used ng-repeat to loop over collection.
We can use the following directives inside the loop:

$index     number     Number of the element      
$first     Boolean     This is true if the element is the first one      
$last     Boolean     This is true if the element is the last one      
$middle     Boolean     This is true if the element is in the middle      
$even     Boolean     This is true if the element is even      
$odd     Boolean     This is true if the element is odd   

we can also have nested loops in that case we can get the parent index using $parent.$index
We have also used ng-src for image to avoid using src directly which will fire 2 calls one before binding (which will fail) and another one after binding.
We used the {{ }} directive to bind values we can also use the directive ng-bing or ng-bindHtml to bind values.
We have used also ng-click to bind onClick event to that element, we can use also the following events:
ng-Blur, ng-Change, ng-Copy, ng-Cut, ng-DblClick, ng-Focus, ng-KeyPress, ng-KeyDown, ng-KeyUp, ng-Mousedown, ng-Mouseenter, ng-Mouseleave, ng-Mousemove, ng-Mouseover, ng-Mouseup, and ng-Paste
We can enable/disabled the element using ng-disabled="boolean value"
The same for ng-show and ng-hide which take boolean expression.
ng-if pevent the rendering of the element if the condition is not true.
We can also import certain parts in our view using ng-include directive.
e.g. <div ng-include="'mainMenu.html'"></div>

Two way Data Binding:
Change in the view update the model and change in the model update the view.
This can be used with input, textfield and text area, using ng-model
So for example in our previous example we can modify the Salary :
          <td> Salary : {{ emp.salary}} <input type="text" ng-model="emp.salary" /> </td>

Filters in AngularJS:
We can use Filters for 3 main use cases; Format, Sort or Filter the data.
To apply a filer using the pipe charachter "|"
e.g. {{ expression | filterName:parameter }}
5 filternames are available: lowercase, uppercase, number, currency and date.

LimitToFilter can be used to limit the number of rows or charachers in a String.
e.g. {{ expression | limitTo : limit : begin }}
$scope.rowLimit=3;
In the view:
Rows to Display: <input type="number" step="1" min="0" max="5" ng-model="rowLimit"/>
<br>
<table>
<thead>
<tr>
    <th>First Name</th>
    <th>Last Name</th>
    <th>Salary</th>
    <th>Tax</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="emp in employees | limitTo:rowLimit:0">
<td> First Name : {{ emp.firstName |  upppercase }}</td>
<td> Last Name : {{ emp.lastName | lowercase }}</td>
<td> Hiredate : {{ emp.hiredate | date:"dd/MM/yyyy"}}</td>
<td> Salary : {{ emp.salary | number:2 }}</td>
<td> Tax : {{ emp.tax | currency:"$":1 }}</td>
</tr>
</tbody>
</table>

To sort data we use {{ orderby_expression | orderBy: expression : reverse }}
e.g. ng-repeat=" emp in employees | orderBy:'salary':false" or '+salary' or '-salary'

$scope.sortColumn = "firstName";
In view:
    Order By : <select ng-model="sortColumn">
                        <option value="firstName"> FirstName Asc</optoin>
                        <option value="-firstName"> FirstName Desc</optoin>
                        <option value="+salary"> Salary Asc</optoin>
                        <option value="-salary"> Salary Desc</optoin>
                    </select>

    <br>
    <table>
    <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Salary</th>
        <th>Tax</th>
    </tr>
    <tr ng-repeat="emp in employees | orderBy:sortColumn">
        <td> First Name : {{ emp.firstName |  uppercase }}</td>
        <td> Last Name : {{ emp.lastName | lowercase }}</td>
        <td> Hiredate : {{ emp.hiredate | date:"dd/MM/yyyy"}}</td>
        <td> Salary : {{ emp.salary | number:2 }}</td>
        <td> Tax : {{ emp.tax | currency:"$":1 }}</td>
    </tr>
    </table>
To sort using column header:
    $scope.reverseSort=false;
    //construct sorting column and reverse flag
    $scope.sortDate= function(column){
      $scope.reverseSort = ($scope.sortColumn == column) ? !$scope.reverseSort : false;
      $scope.sortColumn = column;
    }
    //add function to return css to show arrow beside the column name
    $scope.getSortClass= function(column) {
     if($scope.sortColumn== column) {
        return $scope.reverseSort ? 'arrow-down' : 'arrow-up'
     } else return '';
    }

In the view now we need to define the CSS for the arrow-down and arrow-up.
        <th ng-click="sortDate('firstName')"> First Name <div ng-class="getSortClass('firstName')"/> </th>
        <th ng-click="sortDate('lastName')"> Last Name <div ng-class="getSortClass('lastName')"/> </th>
        <th ng-click="sortDate('salary')"> Salary <div ng-class="getSortClass('salary')"/> </th>
        <th ng-click="sortDate('tax')"> Tax <div ng-class="getSortClass('tax')"/> </th>
<tr ng-repeat="emp in employees | orderBy:sortColumn:reverseSort">
..
</tr>

Finally in css file:
th{
      cursor: pointer;
  }
  .arrow-up {
      width: 0;
      height: 0;
      border-left : 5px solid transparent;
      border-right : 5px solid transparent;
      border-bottom : 10px solid black;
      display: inline-block;
  }
  .arrow-down {
      width: 0;
      height: 0;
      border-left : 5px solid transparent;
      border-right : 5px solid transparent;
      border-top : 10px solid black;
      display: inline-block;
  }
This will enable the sorting using the table headers.

Form Validations:
To mandate a field: ng-required="true"
For min and max we can use
    ng-minlength="6"
        ng-maxlength="10"
We can also add reqular expressions using: ng-pattern
ng-pattern="/^[A-z]+$/"  to restrict it to alpahbets only
If we have form like <form name="personalForm"> and a field like:
<form name="personalForm">
        <div ng-show="personalForm.name.$dirty && personalForm.name.$invalid">Something went wrong! <br>
        <span ng-show="personalForm.name.$error.required">
        You must enter the first name!<br>
        </span>
        <span ng-show="personalForm.name.$error.minlength">
        The name must have at least 6 characters!<br>
        </span>
        <span ng-show="personalForm.name.$error.maxlength">
        The name must have at most 10 characters!<br>
        </span>
        <span ng-show="personalForm.name.$error.pattern">
        The name must only include alphabets!<br>
        </span>
        </div>
        <td> <input type="text" name="name" ng-required="true" ng-model="name" ng-minlength="6" ng-maxlength="10" ng-pattern="/^[A-z]+$/" />
        <input type="button" value="Submit" ng-disabled="personalForm.name.$invalid"/>
  </form>
now we can use attribute to enable or disable the submit button:
        <input type="button" value="Submit" ng-disabled="personalForm.name.$invalid"/>

We can also show alert using the field attribute $invalid
<div ng-show="personalForm.name.$dirty && personalForm.name.$invalid">Something went wrong! <br>


Search Filter:
Same concept using filter:key_word:exact_match
e.g.
First Name : <input type="text" ng-model="searchText.firstName" placeholder="search first name"/>
    Last Name : <input type="text" ng-model="searchText.lastName" placeholder="search last name"/>
    <input type="checkbox" ng-model="exactMatch"> Exact Match

And in the repeat section:
    <tr ng-repeat="emp in employees | filter:searchText:exactMatch">

Custom Filter:
To create custom filter all what we need to do is to define a filter function that return a function:
myApp.filter("genderFilter",function() {
    return function(gender) {
        switch (gender) {
            case 1: return "Male";
            case 2: return "Female";
            default: return "N/A";
        }
    }
});

Now from inside the view use it as :
<td> {{ emp.gender | genderFilter }} </td>


AngularJS Service:
To call a REST service to retireve the data, all you need is the end point and know the
structure of your response data which is mostly JSON.
For example if we have the following end point that retrieve all employees from the DB as a REST service with JSON structure:
http://localhost:8090/ords/my_hr/my_emp/

We can call it using a controller:
myApp.controller("myController2",function($scope,$http){
        $http.get('http://localhost:8090/ords/my_hr/my_emp/')
            .then(function (response) {
                $scope.employees2 = response.data;
            });
});
We have used get HTTP method, we can use POST, DELETE, PUT, etc.
 Alternatively we can use the following way where we send configuration object as a parameter to the $http service:
myApp.controller("myController2",function($scope,$http){
        $http(
            {
            url:'http://localhost:8090/ords/my_hr/my_emp/',
            method: 'get'
             })
            .then(function (response) {
                $scope.employees2 = response.data.items;
            });
});
We can also supply another function for errors:
$http({
            url:'http://localhost:8090/ords/my_hr/my_emp/',
            method: 'get'})
            .then(function (response) {
                $scope.employees2 = response.data.items;
                $log.warn(response.data);
            }, function(reason){
                $scope.error= reason.data;
                $log.error(reason.data);
            });
Where we can get the reason object of such error.
We can also define these functions separately and then assign them in the then() method
e.g. var function successCallBackFunction= function (response){ ... } &
var function errorCallBackFunction= function (reason){ ... } &
Now change then method into :
.then(successCallBackFunction,errorCallBackFunction)

NOTE: if you are using Oracle REST Data Service - ORDS you need to retireve the data using response.data.items;

This will invoke the service and set the data in the scope once the response is available.
In the view you can consume it as usual:
      <tr ng-repeat="emp in employees2">
        <td> {{ emp.first_name}}  </td>
        <td> {{ emp.last_name}}  </td>
        <td> {{ emp.hire_date | date:"dd/MM/yyyy"}}</td>
        <td> {{ emp.salary}}</td>
        <td> {{ emp.email}} </td>
    </tr>

That's the simplest way to interact with REST services.

Use Logging Service:
Simply we need to import the log service in controller parameters
e.g. myApp.controller("myController2",function($scope,$log){

and we can then use it as $log.info('message here'); or use other log messages types such as debug, warn and error.

Building Your Own Service:


Service in AngularJS are JS objects that could contain atributes or functions for example $scope, $http and $log services, to build your own service you need to simply define the service and its attributes/functions so you can use it inside your application.
This should promotes usability inside your application.
For example we can define a new service named : service_name

myApp.factory('service_name',function() {
    return {
        //the JS object here attributes and functions
        helloWorldFunction : function(input) {
            return "Hello "+input;
        }
    };
}
To use it in any controller, all you need is to reference your new service
e.g.
myApp.controller("myController2",function($scope, service_name){
     $scope.greeting= service_name.helloWorldFunction('Osama Oransa');
}

Useuful Tutorial:
One of the most useful AngularJS tutorials is the following one:
https://www.youtube.com/watch?v=zKkUN-mJtPQ&list=PL6n9fhu94yhWKHkcL7RJmmXyxkuFB3KSl
where a lot of the content in this post is based on that tutorial.


No comments:

Post a Comment