LearningAngular

From HerzbubeWiki
Jump to navigation Jump to search

On this page I keep notes about my first steps with the web framework Angular. There is also a section about the predecessor framework AngularJS.

In the context of educating myself about web programming, probably the most relevant companion page to this one is LearningJavaScript.


References


Glossary

Angular
Angular is the successor to AngularJS. The first Angular alpha release on GitHub was 2.0.0-alpha.13 in March 2015. Angular then quickly progressed to version 2.0.0 (September 2016) and since then has released a steady stream of new major versions at the rate of 1 or more per year. At the time of writing this (May 2021) version 12 is the current Angular version.
AngularJS
AngularJS is the predecessor to Angular. Development on AngularJS has ceased, but security fixes will continue until end of 2021.
Template
An HTML document or fragment that contains additional markup that is specific to Angular. The main template is the original HTML document served to the client by the web server. When Angular starts the application, it parses and processes the Angular-specific markup from the template using an internal "compiler". The loaded, transformed and rendered DOM is then called the view. Templates can be defined in various places of an Angular application, e.g. in the definition of a directive or a component.
View
What the user sees (the DOM).
Component
A piece of an Angular application that defines a template/view as well as associated behaviour.
Directive
A custom attribute or element.
Expression
A JavaScript-like code snippet. It references variables and functions from the scope. Appears with double braces {{ ... }} in the markup.
Pipe
Formats the result of an expression.
Data binding
The mechanism that connects the application's data and behaviour and the DOM. There are two kinds of data binding: event binding (lets the app react to events) and property binding (lets the app output its data). Data binding in Angular is two-way, i.e. user input flows back into the application.
Service
Reusable business logic that, unlike a component, is independent of any views.
Routing
Routing is the navigation path among the different application states and view hierarchies in the application. Routing is provided by the Router service, which is a built-in Angular module.
Module
A container for a cohesive set of functionality of an Angular app. Every Angular app has at least one module, the root module.


First impressions

Key points

  • Angular is based on TypeScript.
  • Angular is a component-based framework. A component includes a specially decorated TypeScript class, an HTML template, and styles.


New markup in HTML document

This short and incomplete sample stolen from the Angular tutorial shows a few things about the new markup that we can use in an HTML document:

<div *ngFor="let product of products">
  <h3>
    <a [title]="product.name + ' details'">{{ product.name }}</a>
  </h3>

  <p *ngIf="product.description">
    Description: {{ product.description }}
  </p>

  <p>
    Price: {{ product.price | currency }}
  </p>

  <button (click)="share()">Share</button>

  <app-product-alerts [product]="product" (notify)="onNotify()">
  </app-product-alerts>
</div>

Notes:

  • Attributes like ngFor or ngIf are called "directives". They tell Angular to do something special to the element in question.
  • The asterisk ("*") in front of a directive is shorthand that Angular interprets into a longer form.
  • Double braces {{ ... }} enclose expressions. Angular evaluates these expressions and places the result in the DOM at the site where the expression originally occurred. This process is called interpolation.
  • A pipe character ("|") inside an expression causes the preceding expression to be transformed in some way by the subsequent Pipe. In the example "currency" is an Angular built-in pipe that formats a number as a currency string.
  • An attribute whose name is enclosed in square brackets denotes a property binding. Angular evaluates the attribute value as an expression and uses the result as the actual attribute value.
  • An event attribute whose name is enclosed in parantheses denotes an event binding. Angular binds the event to the method that is the attribute value.
  • An element whose name is not defined in HTML is a placeholder that will be replaced by the output that is generated by an Angular component. Angular determines the component that is responsible for generating the output by finding a component whose "selector" is equal to the placeholder element name. Every component defines a selector, this is a unique name that serves to "select" the component.
  • In the example the component whose selector is "app-product-alerts" is given the "product" object as input. Also the component defines a "notify" output event that is bound to the parent's "onNotify" event handler.


Working with Angular on the command line

See the section CLI Overview and Command Reference section of the Angular docs for details.

To work with the Angular CLI you need to install Node.js, the npm package manager and the Angular CLI npm package.

On macOS with the Homebrew package manager:

brew install node  # this also installs npm
npm install -g @angular/cli


From now on you can use the ng command to invoke Angular.


The following command interactively asks you a few things, then proceeds to create a new folder that contains a barebones working Angular app. Note that this also installs npm packages that are not yet present and initializes a Git repository.

ng new my-app

The following command launches a built-in web server, watches your files, and rebuilds the app as you make changes to those files. The --open option automatically opens your browser to http://localhost:4200/. Note that it can take 10-20 seconds until the browser window pops up. Use the --port nnn option to specify a different port if the default port 4200 is already in use.

ng serve --open

When you're ready for deployment, run the following command. This creates a folder named dist with all the files that need to be deployed to the destination machine that runs the production web server.

ng build


Organization of code

See the section Workspace and project file structure in the Angular docs for all details.

Typical organization of code in an Angular app:

root
+-- src
    +-- app
        +-- component1
        |   +-- component1.component.css
        |   +-- component1.component.html
        |   +-- component1.component.ts
        +-- component2
        |   [...]
        +-- componentN
        |   [...]
        +-- app.component.css
        +-- app.component.html
        +-- app.component.ts
        +-- app.module.ts

Notes:

  • A typical component consists of TypeScript code (.ts file), a template (.html file) and some styles (.css file). The component's files are placed into a dedicated subfolder to keep them together.
  • Every app has a root module. This only consists of a piece of TypeScript code (app.module.ts) that defines some metadata required for bootstrapping the app.
  • The root module contains a root component with the usual three pieces (.ts, .html, .css).


Modules

TODO


Services

TODO


Components

TODO


Templates

TODO


Routing

TODO


HttpClient

TODO


Other topics

Injection token

A provider has to declare what service(s) it provides, a consumer has to declare what service(s) it consumes. The declarations are made via DI (dependency injection) token so that the DI system knows which services to inject into a consumer. There are 3 types of DI tokens:

  • Type token: A type declaration is used as the token.
  • String token: A string is used as the token. This is unreliable because the same string could be used by many different providers, even from different packages / libraries. The order in which imports are made depends how conflicting string tokens are resolved.
  • Injection token: This is used to fix the problems with string tokens. A provider creates and exports a declaration using the type InjectionToken, such as export const FOO = new InjectionToken<string>('foo');. A consumer then imports and uses the declaration.

For more details see this tektutorialshub.com article, or the canonical description on angular.io.


AngularJS

References


Where to obtain AngularJS?

The downloading page at angularjs.org lists the CDN links that you can use to obtain a copy of AngularJS if you don't want to host it yourself (not hosting it yourself is advisable). You can get AngularJS in several variants, the following list shows KB values for AngularJS 1.6.9:

  • Human-readable angular.js: 308.7 KB
  • Minified angular.min.js: 57.87 KB


For instance, the standard snippet to include the minified variant of AngularJS 1.6.9 is this:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>


New markup in HTML document

This short and incomplete sample stolen from the AngularJS docs shows a few things about the new markup that we can use in an HTML document:

<div ng-app ng-init="qty=1;cost=2">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="qty">
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="cost">
  </div>
  <div>
    <b>Total:</b> {{qty * cost | currency}}
  </div>
</div>

Notes:

  • Attributes like ng-app, ng-init or ng-model are called directives. They tell AngularJS to do something special to the element in question.
  • Double braces {{ ... }} enclose expressions. AngularJS evaluates these expressions and places the result in the DOM at the site where the expression originally occurred. As can seen in the example above, expressions frequently refer to models.
  • Inside an expression there can be a filter after the pipe character ("|") to format the output of the preceding expression.


Glossary

Template
An HTML document or fragment that contains additional markup that is specific to AngularJS. The main template is the original HTML document served to the client by the web server. When AngularJS starts the application, it parses and processes the AngularJS-specific markup from the template using an internal "compiler". The loaded, transformed and rendered DOM is then called the view. Templates can be defined in various places of an AngularJS application, e.g. in the definition of a directive.
View
What the user sees (the DOM).
Model
A piece of data shown in the view.
Directive
A custom attribute or element.
Expression
A JavaScript-like code snippet. It references variables and functions from the scope. Appears with double braces {{ ... }} in the markup.
Filter
Formats the result of an expression.
Scope
Context where a model is stored so that controllers, directives and expressions can access it.
Controller
The business logic behind the model.
Data binding
The mechanism that synchronizes data between a model and a view. Data binding in AngularJS is two-way.
Service
Reusable business logic that, unlike a controller, is independent of any views.


ng-app

The ng-app attribute is linked to the ngApp directive. The job of this directive is to initialize the application. The value of the ng-app attribute is a reference to a module. That module defines the configuration to be used for application initialization.

The element on which the ng-app attribute appears is the root element of the application. It can be said to be the owner of the application.

Example:

<body ng-app="foo">

Notes:

  • The root element of an application typically is near the root element of the page, e.g. the body element or even the html element.
  • It is possible to have several applications in one HTML document, however AngularJS automatically initializes only the first ng-app it finds in the document. Additional applications need to be manually bootstrapped (with angular.bootstrap).
  • Applications cannot be nested


Modules part 1

A module definition looks something like this:

var foo = angular.module("module-name", ["dependency-1", "dependency-2", ...])
  [...]

Notes:

  • A module defines other modules that it depends on in an array. The array can be empty. If it's not empty, the AngularJS injector automatically loads the specified dependent modules.
  • The module referenced by the ng-app attribute is responsible for bootstrapping the application


Directives

A directive is defined as part of a mdoule. A directive does something to an element to which it is applied. A directive is identified by its name. The directive name ...

  • ... is case sensitive
  • ... uses camelCase


The AngularJS compiler scans the template document for elements that match any of the directives that the compiler knows about. The compiler normalizes all element and attribute names it encounters in a template and then compares the normalized names against its internal list of known directive names. Normalization works like this:

  • Strip the following prefixes from the element or attribute name: x- and data-. Example: data-foo and x-foo both convert into foo.
  • Convert the following delimiters in the element or attribute name into camelCase: ":", "-" and "_". Example: ng-bind, ng_bind and ng:bind all convert into ngBind.


A directive can appear as follows:

  • As an element name (E). Example: <my-directive></my-directive>.
  • As an attribute name (A). Example: <span my-directive="foo"></span>.
  • As a class name (C). Example: <span class="my-directive: foo;"></span>.
  • As a comment (M). Example: <!-- directive: my-directive foo -->


Best practices:

  • Use the dash ("-") delimiter
  • Use a prefix to prevent clashes with future HTML element names
  • Use directives in element and attribute names, not in class names or comments
  • Use the data- prefix for directive attributes if the HTML template document should be valid HTML


ng_init

The ng-init attribute matches an element to the ngInit directive. The job of this directive is to initialize variables in the scope in which the matched element appears. The attribute value defines the initialization expression.

Example:

<div ng-app="foo" ng-init="foo=42;bar=17">

It is better to initialize variables inside a controller constructor, therefore ng-init is not frequently used.


ng-model part 1

The ng-model attribute matches an element to the ngModel directive. The job of this directive is to bind the value of an input, select, textarea or a custom form control to a variable in the scope in which the matched element appears. The variable name is defined by the attribute value.

Example:

<input type="text" ng-model="foo" />


ng-bind

The ng-bind attribute matches an element to the ngBind directive. The job of this directive is to replace the text content (innerText) of the matched element with the value of an expression. The attribute value contains the expression without double braces.

Example where the expression simply refers to a variable named "foo":

<p ng-bind="foo"></p>


A simpler alternative is to write the same expression with double brace syntax:

<p>{{ foo }}</p>


Expressions

Expressions can be written inside double braces:

{{ 1 + 2 }}

Expressions can also be written as the value of an ng-bind attribute:

Double brace syntax can appear pretty much wherever you like, and AngularJS will replace the expression with its computed value. The following example is shamelessly stolen from the w3schools tutorial:

<div ng-app="" ng-init="myColor='lightblue'">
<input style="background-color:{{myColor}}" ng-model="myColor" value="{{myColor}}" />
</div>


You can use numbers, strings, objects and arrays in the same way as you would in normal JavaScript:

<!-- Numbers example -->
<div ng-app="theApp" ng-init="foo=42;bar=17">
<p>{{ foo * bar }}</p>
</div>

<!-- Strings example -->
<div ng-app="theApp" ng-init="firstName='John';lastName='Doe'">
<p>The name is {{ firstName + " " + lastName }}</p>
</div>

<!-- Object example -->
<div ng-app="theApp" ng-init="person={firstName:'John',lastName:'Doe'}">
<p>The name is {{ person.firstName + " " + person.lastName }}</p>
</div>

<!-- Array example -->
<div ng-app="theApp" ng-init="anArray=[42,17]">
<p>The second element is {{ anArray[1] }}</p>
</div>


Note: AngularJS expressions do not support conditionals, loops, and exceptions. The only logic that is possible is the ternary operator.


ng-controller

The ng-controller attribute matches an element to the ngController directive. The job of this directive is to attach a controller to the matched element. The attribute value contains the name of the controller.

Example:

<div ng-app="foo" ng-controller="fooController">


Actually the controller name refers to a function which is defined as part of a module. Example:

var fooModule = angular.module("foo", []);
fooModule.controller("fooController", function($scope) {
  $scope.bar = "baz";
});

Notes:

  • The controller function is invoked with a parameter $scope, which is a scope object to which the controller can add properties
  • The view and the model have access to these properties via the scope
  • It is not by accident that the parameter has a "$" character prefix and is named "scope". The reason is the dependency injection system of AngularJS: The DI system inspects the function definition on the source code level and assigns special meaning to the parameter name $scope. It doesn't matter which position the parameter appears in, the DI system will pass in the scope object at the correct position. This effectively creates a system of named parameters, replacing JavaScript'language-level positional parameter system.


Scope

The scope connects directives/views and their controllers:

  • Directives/views get their values from a scope. A directive observes the scope via a thingy called $watch and updates the view when a change occurs.
  • Controllers manipulate variables in a scope, represented by a scope object

Scopes are decoupling controllers and directives/views: A controller operates solely on the data in the scope without having to know anything about how the data is presented (by a view) nor how the DOM is structured.

I probably should not confuse the two, but at the moment I continually interchange "scope" and "model" in my mind.

An application has exactly one root scope, which is the scope created on the HTML element that contains the ng-app attribute. Controllers and certain directives (e.g. ng-repeat) create their own scopes, which are descendants of the root scope. Scopes are hierarchically structured, with parent/child relationships reflecting the way how the DOM is structured.

When AngularJS needs to obtain a property value from a scope, it first searches the scope of the immediate context. If it cannot find the property there, it then navigates the scope tree upwards towards the root scope until it finds the desired property. Thus, the root scope is available in the entire application.


Modules part 2

A module is the container for various other things:

  • Controllers
  • Directives
  • Services
  • Filters
  • Configuration information

All things in a module are created by factory functions. A factory function is executed only the first time that AngularJS encounters a controller or directive reference.


Example:

var fooModule = angular.module("foo", []);

fooModule.directive("myDirective", function() {
  var directiveObject = {
    // The "restrict" property defines where the AngularJS compiler looks
    // for elements that match the directive.
    // E = element name, A = attribute name, C = class, M = comment
    restrict : "EA",

    // The "template" property specifies an HTML fragment that completely
    // replaces the matched element's content (innerHTML)
    template : "<p>The sum is {{ aNumber * anotherNumber }}</p>",

    // The "templateUrl" property is used instead of the "template" property
    // if the template is too complex to specify in a simple string
    templateUrl : "the-sum.html",

    // A function can be used if the URL is non-static. The element parameter
    // refers to the element matched by the directive, the attributes parameter
    // contains the attributes on the matched element. To access an attribute
    // "bar" you would say "attributes.foo".
    templateUrl : function(element, attributes) {
      var url = ...;
      return url;
    },

    // If a controller is needed but it is not specified in the template HTML,
    // it's possible to specify it here either by name ...
    controller : "sumController",
    // ... or by function
    controller : ['$scope', function($scope) {
      [...]
    }]
  };

  return directiveObject;
});

fooModule.info(
  // A custom object that provides information about the module
  {
    version : "1.0.0"
    description : "provides a fabulous service";
  }
);

Notes:

  • In this example the module does not specify any dependencies
  • TODO Add examples for the remaining things that can be part of a module


Module definitions can be chained because each module function returns an instance of the module. However, this chaining requires that the entire module definition is in one place. Chaining cannot be used if you want to distribute the various parts of a module across several .js files. Example:

var fooModule = angular
  .module("foo", [])
  .controller("controller1", [...] )
  .controller("controller2", [...] )
  .directive("directive1", [...] )
  [...]


ng-show / ng-hide

The ng-show attribute matches an element to the ngShow directive. The job of this directive is to show or hide the matched element depending on the value of an expression. The attribute value contains the expression. If the expression evaluates to truthy the element is shown, if it evaluates to falsy the element is hidden.

The ng-hide attribute works the same as ng-show but uses inverted logic.


ng-model part 2

The ng-model directive provides status information about model data. The stati are all boolean values. Example:

<form ng-app="foo" name="myForm">
<label for="email">Email:</label>
<input id="email" type="email" name="myEmailAddress" ng-model="email" required>

<h1>Status</h1>
<p>is valid = {{myForm.myEmailAddress.$valid}}</p>
<p>is dirty = {{myForm.myEmailAddress.$dirty}}</p>
<!-- If the field has been in focus, i.e. becomes true after the field loses focus for the first time -->
<p>is touched = {{myForm.myEmailAddress.$touched}}</p>
</form>


The ng-model directive also applies CSS classes to the matched element depending on the status. These can be used in a stylesheet for great effect. These are the CSS classes:

  • ng-empty
  • ng-not-empty
  • ng-touched
  • ng-untouched
  • ng-valid
  • ng-invalid
  • ng-dirty
  • ng-pending
  • ng-pristine


The ng-model directive provides validation support which can be used together with ng-show. Building on the initial example form, the following example shows an error message only if the entered email-address is not valid in some way.

<span ng-show="myForm.myEmailAddress.$error.email">Not a valid e-mail address</span>


Templates

TODO


ng-repeat

The ng-repeat attribute matches an element to the ngRepeat directive. The job of this directive is to clone the matched element once for each element in a collection. The attribute value specifies the collection, and expressions in the matched element refer to the elements of the collection.

Examples:

<div ng-app="foo" ng-init="items=['Foo','Bar','Baz']">
  <ul>
    <li ng-repeat="item in items">{{ item }}</li>
  </ul>

  <select size="1" ng-model="foo">
    <option ng-repeat="fooOption in fooOptions" value="{{ fooOption.optionValue }}">{{ fooOption.optionText }}</option>
  </select>
</div>

Notes:

  • Unrelated to ngRepeat but still noteworthy for the "select" example: AngularJS uses strict comparison (===) to match model values with option values. Because option values are always string values the model default value must also be a string value if the "select" element is to have an initial selection. There are ways to to this differently, for details read the AngularJS docs about the select directive.


ng-click

The ng-click attribute matches an element to the ngClick directive. The job of this directive is to respond to the user clicking or tapping on the matched element. The attribute value specifies an expression to evaluate. The expression can directly manipulate model data, or - if the required logic is more complex - it can invoke an event handler function defined in the controller.

Example:

<button ng-click="count = count + 1" />
<button ng-click="foo()" />
<button ng-click="bar($event)" />

var aModule = angular.module("aModule", []);
aModule.controller("aController", function($scope) {
  $scope.foo = function() { [...] }
  $scope.bar = function($event)
  {
    var clickedElement = $event.target;
    var clickedElementJQuery = $(clickedElement);
    [...]
  }
});

Notes:

  • Within the expression, AngularJS exposes an object $event which represents the click event. Notice the prefixing dollar character ("$").


ng-class

The ng-class attribute matches an element to the ngClass directive. The job of this directive is to set or clear one or more classes on the matched element. The attribute value specifies an expression to evaluate. The way how ngClass behaves depends on the expression's result type.

Example:

<div ng-class="classA classB" />
<div ng-class="{ 'current-player': isThisPlayersTurn }" />
<div ng-class="["classA classB", { 'current-player': isThisPlayersTurn }]" />

Notes:

  • In the first example the expression evaluates to a string. In this case ngClass simply applies all the resulting classes to the element. Multiple classes must be space-delimited.
  • In the second example the expression evaluates to an object. The object's keys are class names, the object's values are truthy/falsy values. ngClass applies all classes that have a truthy value, and clears all classes that have a falsy value.
  • In the third example the expression evaluates to an array. Each array element can be either a string as in example 1, or an object as in example 2.


Modifying data when non-AngularJS events fire

TODO: Explain the problem, then show the $apply solution.


Dependency injection

Controllers (and other AngularJS elements) may have dependencies. AngularJS allows to specify those dependencies when a controller is declared. When the controller's factory function is invoked, AngularJS automatically supplies the specified dependencies as parameters to the factory function.

Example:

aModule.controller("FooController", ["$scope", "dep1", "dep2", function($scope, dep1, dep2) {
  [...]
}]);

Notes:

  • The example uses the so-called "inline array annotation" syntax to specify dependencies. This syntax makes the code safe to be minified because the strings are not touched by the minifier.
  • The order of dependencies in the array and in the function declaration must be the same
  • "$scope" is a special depedency name that causes a scope object to be injected
  • "dep1" and "dep2" can refer to anything from the following list:
    • A value
    • A constant
    • A factory
    • A service
    • A provider


Factories, services and providers are discussed in other sections on this page, but here's a short example how to define values and constants:

aModule.value("foo", 42);
aModule.constant("bar", 17);


Services

Example:

aModule.service("CalcService", function() {
  this.square = function(a) { return a * a; }
});

aModule.controller("CalcController", ["$scope", "CalcService", function($scope, CalcService) {
   $scope.number = 42;
   $scope.result = CalcService.square($scope.number);
}]);

Notes:

  • The factory function supplied to the service() function does not create and return something new - the factory function IS the service. AngularJS creates a single object of the service using new and then passes that single object as the dependency to the controller factory function (and every other AngularJS element that specifies a dependency on the service).
  • For this reason, the service factory function declares service functions and properties by attaching them to the this object.
  • The service factory function can also use local variables and functions for private data and functions that should not be accessible by service consumers.
  • By using a parameter name in the controller factory function that begins with an uppercase letter, it is immediately apparent that the code using the service is using something globally unique.


Factories

Example:

aModule.factory("mathObject", function() {
  var mathObject = {};

  mathObject.multiply = function(a, b) {
    return a * b
  }

  return mathObject;
});

aModule.controller("MathController", ["$scope", "mathObject", function($scope, mathObject) {
   $scope.number1 = 42;
   $scope.number2 = 17;
   $scope.result = mathObject.multiply($scope.number1, $scope.number2);
}]);

Notes:

  • The factory function supplied to the factory() function must create and return a new object. AngularJS invokes the factory function and passes the RESULT of the call as the dependency to the controller factory function.
  • In contrast to the service example in the previous section, we use a parameter name in the controller factory function that begins with a lowercase letter, to indicate that we are using an object that is not globally unique.


Factories can be used (or misused?) to create something that looks and acts like a service, because the factory function can have local variables that act as private data repositories that the individual objects created by the factory can access. To me, however, this looks a little bit like a hack.

aModule.factory("FooService", function() {
  var globalState = 42;

  var fooObject = {};

  fooObject.getGlobalState { return globalState; }
  fooObject.setGlobalState(_globalState) { globalState = _globalState; }

  return fooObject;
});


Providers

TODO Show an example. The main difference between providers and services is that providers can be changed during app configuration.


Module configuration

Early on during its lifecycle, a module can be configured:

aModule.config(["dep1", "dep2", function(dep1, dep2) {
  [...]
}]);

Notes:

  • A module can have multiple config blocks, they do not overwrite each other
  • Only providers and constants can be specified as dependencies in a config block


Routing

To be able to use routing with the ngRoute directive, you have to load the AngularJs Route module and specify the dependency when you create the app object:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-route.js"></script>

var aModule = angular.module("aModule", ["ngRoute"]);


The Route module provides the $routeProvider provider. During module configuration, $routeProvider can be configured with what should happen when the user navigates to different URL paths. Note that the following is only a very basic example. There are many ways how the specified path and controller can be made flexible - see https://docs.angularjs.org/api/ngRoute/provider/$routeProvider for details.

aModule.config(["$routeProvider", function($routeProvider) {
  $routeProvider
  .when("/", {
    templateUrl : "main.html"
  })
  .when("/foo", {
    templateUrl : "foo.html",
    controller  : "fooController"
  })
  .when("/bar", {
    templateUrl : "bar.html"
    controller  : "barController"
  })
  .otherwise( { redirectTo: "/" } ) ;
});


Finally, the content to be displayed must be placed somewhere. This is specified with the ngView directive. An AngularJS application can have only one such directive, and it acts as the placeholder for all the views that result from routing.

<ng-view></ng-view>