Showing posts with label angularjs. Show all posts
Showing posts with label angularjs. Show all posts

Tuesday, June 10, 2014

Can AngularJS have multiple ng-app directives in a single page?


The answer is NO. The ng-app directive is used to auto-bootstrap an AngularJS application. And according to AngularJS Documentation, only one AngularJS application can be auto-bootstrapped per HTML document. I'd like to quote a section from the documentation here:

Only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead. AngularJS applications cannot be nested within each other.

I'm going to show three approaches, latter two being similar, to deal with the issue.

Before that let's take a look what the actual problem is.
<!DOCTYPE html>

<html>
<head>
<title>Multiple ngApp does not work</title>
</head>

<body>
       <div ng-app="firstApp">
              <div ng-controller="FirstController">
                 <p> 1# {{ name }} </p>
              </div>
       </div>

       <div ng-app="secondApp">
              <div ng-controller="SecondController">
                 <p> 2# {{ name }} </p>
              </div>
       </div>

       <script src="angular.js"></script>
       <script type="text/javascript">
              
            var firstApp = angular.module('firstApp', []);
            firstApp.controller('FirstController', function($scope) {
            
                 $scope.name = "I'm the first app.";
            });

            var secondApp = angular.module('secondApp', []);
            secondApp.controller('SecondController', function($scope) {
            
                $scope.name = "I'm the second app.";
            });
       </script>
</body>
</html>

As you can see, we defined two separate ng-app directives named firstApp and secondApp. And we also defined two controllers one for each ng-app module and each having its own scope variable name. As the documentation states, only the first ng-app module is auto-bootstrapped. So, in this case, only the firstApp module works as expected while the secondApp module does not. As a consequence, the browser renders the above page as follows:


1# I'm the first app.
2# {{ name }} 

Now that we discussed the problem, let's move ahead and see how to use alternatives.

Method 1: Injecting modules as dependencies of the root app

The idea here is to define only one top level ng-app in a root element like in <html> or <body>, define other two as modules and inject them as dependencies of the root app.
<!DOCTYPE html>

<html>
<head>
       <title>Injecting modules as dependencies of the root app </title>
</head>

<body ng-app="rootApp">

<div id="firstApp">
       <div ng-controller="FirstController">
              <p>1# {{ name }}</p>
       </div>
</div>

<div id="secondApp">
       <div ng-controller="SecondController">
              <p>2# {{ name }}</p>
       </div>
</div>

<script src="angular.js"></script>
<script type="text/javascript">

    // passing the two modules as dependencies of the root module
    var rootApp = angular.module('rootApp', ['firstApp','secondApp']);

    var firstApp = angular.module('firstApp', []);
         firstApp.controller('FirstController', function ($scope) {
         $scope.name = "I'm the first app.";
    });

    var secondApp = angular.module('secondApp', []);
    secondApp.controller('SecondController', function ($scope) {
         $scope.name = "I'm the second app.";
    });      

</script>
</body>
</html>
This will give the desired result:

1# I'm the first app.
2# I'm the second app.


Method 2: Manual bootstrapping the second module

In this method, we are going to leave the first ng-app as it is so that it is auto-bootstrapped by Angular. Whereas for the second ng-app, we're going to a manual bootstrapping method.
<!DOCTYPE html>

<html>
<head>
      <title>Manual bootstrapping the second module</title>
</head>

<body>
       <div ng-app="firstApp">
              <div ng-controller="FirstController">
                     <p>1# {{ name }}</p>
              </div>
       </div>

       <!-- using id attribute instead of ng-app -->
       <div id="secondApp">
              <div ng-controller="SecondController">
                     <p>2# {{ name }}</p>
              </div>
       </div>

<script src="angular.js"></script>
<script type="text/javascript">
      
       var firstApp = angular.module('firstApp', []);
       firstApp.controller('FirstController', function($scope) {

              $scope.name = "I'm the first app.";
       });

       var secondApp = angular.module('secondApp', []);
       secondApp.controller('SecondController', function($scope) {

              $scope.name = "I'm the second app.";
       });

       // manually boostrapping the second app
       var secondDiv = document.getElementById('secondApp');

       angular.element(document).ready(function() {
              angular.bootstrap(secondDiv, [ 'secondApp' ]);
       });
</script>
</body>
</html>

Method 3: Manual bootstrapping both modules

As I already mentioned, this method is similar to the previous one. Here, we don't rely on Angular's auto-bootstrapping to initialize the modules. We'll use manual bootstrapping method to initialize both modules as depicted in the example below:
<!DOCTYPE html>

<html>
<head>
      <title>Manual boostrapping both modules</title>
</head>

<body>
       <!-- using id attribute instead of ng-app -->
       <div id="firstApp">
              <div ng-controller="FirstController">
                     <p>1# {{ name }}</p>
              </div>
       </div>

       <!-- using id attribute instead of ng-app -->
       <div id="secondApp">
              <div ng-controller="SecondController">
                     <p>2# {{ name }}</p>
              </div>
       </div>

<script src="angular.js"></script>
<script type="text/javascript">
       var firstApp = angular.module('firstApp', []);
       firstApp.controller('FirstController', function($scope) {

              $scope.name = "I'm the first app.";
       });

       var secondApp = angular.module('secondApp', []);
       secondApp.controller('SecondController', function($scope) {

              $scope.name = "I'm the second app.";
       });

       var firstDiv = document.getElementById('firstApp');
       var secondDiv = document.getElementById('secondApp');

       // manually boostrapping the second app
       angular.element(document).ready(function() {
              angular.bootstrap(firstDiv, [ 'firstApp' ]);
              angular.bootstrap(secondDiv, [ 'secondApp' ]);
       });
</script>
</body>
</html>

Sunday, June 8, 2014

Getting Started with AngularJS



Before starting anything with Angular JS, make sure to include the AngularJS library in your page. You can download the latest version of AngularJS from its official site: angularjs.org.
<html>
<body>
       <!-- rest of the stuffs here -->

       <script src="angular.js"></script>
</body>
</html>

Now, we're ready to go.

Without spending much time on the traditional intro sections, let's get started with the major components of AngularJS.

Directives

These are one of the major components of AngularJS. A directive is a marker on a HTML tag that tells Angular to run or reference some javascript code. It provides us new ways to represent HTML DOM elements by adding attributes, element names, comments and CSS classes. These directives are processed by Angular's HTML Compiler. So, the directives tell the compiler to attach specified behaviour or event listeners to that DOM element or even transform the DOM element and its children, thereby creating interactive HTML.

Angular comes with a set of these directives built-in, like ngApp or ng-app, ngRepeat or ng-repeatngModel or ng-model, ngView or ng-view and many more. All these directives attach special behavior to DOM elements. For instance, the ngApp directive specifies that the specified element is the root element of the application. The ngRepeat directive iterates over a collection.You can define your own custom directives as well. We'll see how to create custom directives later on.

The following demonstrates the various ways a directive (myDir in this case) can be referenced from within a template:

Element Name
<my-dir></my-dir>
Attribute
<div my-dir="exp"></div>
Comment
<!-- directive: my-dir exp -->
CSS class
<div class="my-dir: exp;"></div>


Normalization

Angular normalizes an element's name and attribute name to determine which elements match which directives. We typically refer to directives by their case-sensitive camelCase normalized name (e.g. ngModel). However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case forms, typically using dash-delimited attributes on DOM elements (e.g. ng-model).

The normalization process is as follows:
  • Strip x- and data- from the front of the element/attributes.
  • Convert the  : ,  - , or  _  -delimited name to camelCase.

Here are some equivalent examples of elements that match ngBind:
ng-bind
ng:bind
ng_bind
data-ng-bind
x-ng-bind

Let's look at some directives examples:

# Example 1: Directives and Data Binding

<!DOCTYPE html>

<html ng-app>
<head>
       <title>Directives and Data Binding Example 1</title>
</head>
<body>
       Name:
       <br />
       <input type="text" ng-model="fullName" /> {{ fullName }}

       <script src="angular.js"></script>
</body>
</html>

Output:

Name:
{{fullName}}

ng-app :  The ngApp directive designates the root element of the application and is typically placed near the root element of the page - e.g. on the <html> or <body> tags. By including the ngApp directive, Angular treats the HTML inside that element as parts of anguluar application.

ng-model :  The ngModel directive binds an input,select, textarea (or custom form control) to a property on the scope using built-in ngModelController. It tries to bind to the property given by evaluating the expression on the current scope. If the property doesn't already exist on this scope, it will be created implicitly and added to the scope. So, in the above example, Angular adds a property "fullName" into the scope.

{{ expression }} :  These are called Angular Expressions. Angular expressions are JavaScript-like code snippets that are usually placed in bindings such as {{ expression }}. It allows you to insert dynamic values to your HTML. You can perform numerical operations, string operations etc. For instance, {{ 1 + 1 }} evaluates to 2, {{ "hello" + " world!" }} evaluates to hello world!. The more usefulness of the Angular expressions comes with the data binding. In the above example, it evaluates the property named "fullName" from the scope and renders it to the view in realtime. This is the beauty of data-binding. It synchronizes the data between the model and view components such that any changes to view components updates corresponding model and also any changes to the model updates corresponding view components.

# Example 2: Looping with ng-repeat directives

<div ng-init="phones = [{name: 'HTC One M8', price:'649'},
                        {name: 'Samsung Galaxy S5', price:'699'},
                        {name: 'Sony Xperia Z1', price:'690'},
                        {name: 'Nexus 5', price:'399'},
                        {name: 'iPhone 5S', price:'749'}]">

       <input type="text" ng-model="phoneFilter" placeholder="Filter..." />
       <ul class="phone-list">
              <li ng-repeat="phone in phones | filter: phoneFilter | orderBy: 'price'">
                  
                   {{phone.name}} 
                   <em class="push-right">{{phone.price | currency}}</em>
              </li>
       </ul>
</div>
Output:


  • {{phone.name}} {{phone.price | currency}}

ng-init: The ngInit directive allows you to evaluate an expression in the current scope. It initializes data and can be accessed withing the scope. In the above example, we defined an array of objects named 'phones'.

ng-repeat: The ngRepeat directive instantiates a template once per item from a collection. Each template instance gets its own scope, where the given loop variable is set to the current collection item, and $index is set to the item index or key. In a simple term, it allows you to iterate through a collection. Some special properties such as $first, $last, $middle, $even, $odd etc. are exposed on the local scope of each template instance. In the above example, we defined a loop variable phone that iterates over the collection phones that we put into the scope using the ng-init directive. And it renders the name and price property using data-binding expressions. 

filter: It selects a subset of items from array and returns it as a new array. In the above example, we applied the phoneFilter filter which is a model representing the input field value.

orderBy: It's a filter that orders a specified array by the expression predicate. It orders the collection alphabetically if strings and numerically if numbers.

currency: It's a filter that formats a number as a currency (i.e. $1,234.56). When no currency symbol is provided, default symbol for current locale is used.

There are many other useful built-in filters such as uppercase, lowecase, date etc. You can go to the documentation for the full list of available filters: