Unit 5 MSD M
Unit 5 MSD M
Unit 5 MSD M
Unit-5
Angular JS
Why Angular?
Angular 1 is a JavaScript framework from Google which was used for the development of web
applications.
Following are the reasons to migrate from Angular 1 to the latest version of Angular:
Cross-Browser Compliant
Angular helps to create cross-browser compliant applications easily.
Typescript Support
Angular is written in Typescript and allows the user to build applications using Typescript. Typescript is
a superset of JavaScript and more powerful language. The use of Typescript in application development
improves productivity significantly.
Web Components Support
Component-based development is pretty much the future of web development. Angular is focused on
component-based development. The use of components helps in creating loosely coupled units of
application that can be developed, maintained, and tested easily.
Better support for Mobile App Development
Desktop and mobile applications have separate concerns and addressing these concerns using a single
framework becomes a challenge. Angular 1 had to address the concerns of a mobile application using
additional plugins. However, the Angular framework, addresses the concerns of both mobile as well as
desktop applications.
Better performance
The Angular framework is better in its performance in terms of browser rendering, animation, and
accessibility across all the components. This is due to the modern approach of handling issues compared
to earlier Angular version 1.x.
What is Angular?
Single Page Application (SPA)
A Single Page Application (SPA) is a web application that interacts with the user by dynamically
redrawing any part of the UI without requesting an entire new page from the server.
For example, have a look at the Amazon web application. When you click on the various links present in
the navbar present in any of the web pages of this application, the whole page gets refreshed. This is
because visibly, a new request is sent for the new page for almost each user click. You may hence
observe that it is not a SPA.
But, if you look at the Gmail web application, you will observe that all user interactions are being
handled without completely refreshing the page.
Modern web applications are generally SPAs. SPAs provide a good user experience by
communicating asynchronously (a preferable way of communication) with a remote web server
(generally using HTTP protocol) to dynamically check the user inputs or interactions and give constant
feedback to the user in case of any errors, or wrongful/invalid user interaction. They are built block-by-
block making all the functionalities independent of each other. All desktop apps are SPAs in the sense
that only the required area gets changed based on user requests.
Angular helps to create SPAs that will dynamically load contents in a single HTML file, giving the user
an illusion that the application is just a single page.
Let us now understand what is Angular and what kind of applications can be built using Angular:
Angular is an open-source JavaScript framework for building both mobile and desktop web
applications.
Angular is exclusively used to build Single Page Applications (SPA).
Angular is completely rewritten and is not an upgrade to Angular 1.
Developers prefer TypeScript to write Angular code. But other than TypeScript, you can also
write code using JavaScript (ES5 or ECMAScript 5).
Features of Angular:
Easier to learn: Angular is more modern and easier for developers to learn. It is a more
streamlined framework where developers will be focusing on writing JavaScript classes.
Good IDE support: Angular is written in TypeScript which is a superset of JavaScript and
supports all ECMAScript 6 features. Many IDEs like Eclipse, Microsoft Visual Studio, Sublime
Text, etc., have good support for TypeScript.
Familiar: Angular has retained many of its core concepts from the earlier version (Angular 1),
though it is a complete re-write. This means developers who are already proficient in Angular 1
will find it easy to migrate to Angular.
Cross-Platform: Angular is a single platform that can be used to develop applications for
multiple devices.
Performance: Angular performance has been improved a lot in the latest version. This has been
done by automatically adding or removing reflect metadata from the polyfills.ts file which makes
the application smaller in production.
Lean and Fast: Angular application's production bundle size is reduced by 100s of kilobytes due
to which it loads faster during execution.
Bundle Budgets: Angular will take advantage of the bundle budgets feature in CLI which will
warn if the application size exceeds 2MB and will give errors if it exceeds 5MB. Developers can
change this in angular.json.
Simplicity: Angular 1 had 70+ directives like ng-if, ng-model, etc., whereas Angular has a very
less number of directives as you use [ ] and ( ) for bindings in HTML elements.
Component-based:
o Angular follows component-based programming which is the future of web development. Each
component created is isolated from every other part of our application. This kind of
programming allows us to use components written using other frameworks.
o Inside a component, you can write both business logic and view.
o Every Angular application must have one top-level component referred to as 'Root Component'
and several sub-components or child components.
Angular places itself on the client-side in the complete application stack and provides a completely
client-side solution for quick application development. Angular has absolutely no dependencies and also
jells perfectly with any possible server-side technology like Java, NodeJS, PHP, etc., and any database
like MongoDB, MySql.
Angular CLI
Visual Studio Code
Angular CLI is a command-line interface tool to build Angular applications. It makes application
development faster and easier to maintain.
Using CLI, you can create projects, add files to them, and perform development tasks such as testing,
bundling, and deployment of applications.
Command Purpose
npm install -g @angular/cli Installs Angular CLI globally
ng new <project name> Creates a new Angular application
Builds and runs the application on lite-server and launches a
ng serve --open
browser
Creates a class, component, directive, interface, module, pipe, and
ng generate <name>
service
ng build Builds the application
ng update @angular/cli
Updates Angular to latest version
@angular/core
You will learn the Angular course by building the mCart application. Below is the roadmap to achieve it.
Angular CLI is a command-line interface tool to build Angular applications. It makes application
development faster and easier to maintain.
Using CLI, you can create projects, add files to them, and perform development tasks such as testing,
bundling, and deployment of applications.
Command Purpose
npm install -g @angular/cli Installs Angular CLI globally
ng new <project name> Creates a new Angular application
Builds and runs the application on lite-server and launches a
ng serve --open
browser
Creates class, component, directive, interface, module, pipe, and
ng generate <name>
service
ng build Builds the application
ng update @angular/cli
Updates Angular to a newer version
@angular/core
2. The above command will display two questions. The first question is as shown below. Typing 'y' will
create a routing module file (app-routing.module.ts).
This will create the following folder structure with the dependencies installed inside the node_modules
folder.
Note: If the above command gives errors while installing dependencies, navigate to the project folder in
the Node command prompt and run "npm install" to install the dependencies manually.
File / Folder Purpose
node_modules/ Node.js creates this folder and puts all npm modules installed as listed in package.json
For example, observe in the mCart application the topmost component is the mCart
component(AppComponent) which consists of child components called Welcome component, Login
component, etc.
Creating a Component
Open Visual Studio Code IDE. Go to the File menu and select the "Open Folder" option. Select
the MyApp folder you have created earlier.
Observe for our AppComponent you have below files
o app.component.ts
o app.component.html
o app.component.css
Line 3: Adds component decorator to the class which makes the class a component
Line 4: Specifies the tag name to be used in the HTML page to load the component
Line 5: Specifies the template or HTML file to be rendered when the component is loaded in the HTML
page. The template represents the view to be displayed
Line 6: Specifies the stylesheet file which contains CSS styles to be applied to the template.
Line 8: Every component is a class (AppComponent, here) and export is used to make it accessible in
other components
Line 9: Creates a property with the name title and initializes it to value 'AngDemo'
Open app.component.html from the app folder and observe the following code snippet in that file
1. <span>{{ title }} app is running!</span>
Line 1: Accessing the class property by placing property called title inside {{ }}. This is called
interpolation which is one of the data binding mechanisms to access class properties inside the template.
Best Practices - Coding Style Rules
Always write one component per file. This will make it easier to read, maintain, and avoid hidden
bugs. It makes code reusable and less mistake-prone.
Always define small functions which makes it easier to read and maintain
The recommended pattern for file naming convention is feature.type.ts. For example, to create a
component for Login, the recommended filename is login.component.ts. Use the upper camel case for
class names. For example, LoginComponent.
Use a dashed case for the selectors of a component to keep the names consistent for all custom
elements. Ex: app-root
Modules
Modules in Angular are used to organize the application. It sets the execution context of an
Angular application.
A module in Angular is a class with the @NgModule decorator added to it. @NgModule
metadata will contain the declarations of components, pipes, directives, services that are to be
used across the application.
Every Angular application should have one root module which is loaded first to launch the
application.
Submodules should be configured in the root module.
Root Module
In the app.module.ts file placed under the app folder, you have the following code:
1. import { BrowserModule } from '@angular/platform-browser';
2. import { NgModule } from '@angular/core';
3.
4. import { AppRoutingModule } from './app-routing.module';
5. import { AppComponent } from './app.component';
6.
7. @NgModule({
8. declarations: [
9. AppComponent
10. ],
11. imports: [
12. BrowserModule,
13. AppRoutingModule
14. ],
15. providers: [],
16. bootstrap: [AppComponent]
17. })
18. export class AppModule { }
Line 1: imports BrowserModule class which is needed to run the application inside the browser
Line 2: imports NgModule class to define metadata of the module
Line 5: imports AppComponent class from app.component.ts file. No need to mention the .ts extension as
Angular by default considers the file as a .ts file
Line 8: declarations property should contain all user-defined components, directives, pipes classes to be
used across the application. We have added our AppComponent class here
Line 11: imports property should contain all module classes to be used across the application
Line 15: providers' property should contain all service classes. You will learn about the services later in
this course
Line 16: bootstrap declaration should contain the root component to load. In this example,
AppComponent is the root component that will be loaded in the HTML page
Bootstrapping Root Module
In the main.ts file placed under the src folder, observe the following code:
1. import { enableProdMode } from '@angular/core';
2. import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3.
4. import { AppModule } from './app/app.module';
5. import { environment } from './environments/environment';
6.
7. if (environment.production) {
8. enableProdMode();
9. }
10.
11. platformBrowserDynamic().bootstrapModule(AppModule)
12. .catch(err => console.error(err));
Line 11: loads the root component in the HTML page. app-root is the selector name given to the
component. This will execute the component and renders the template inside the browser.
Best Practices - Coding Style Rules
Always add a Module keyword to the end of the module class name which provides a consistent way
to quickly identify the modules. Ex: AppModule.
Filename convention for modules should end with module.ts
Always name the module with the feature name and the folder it resides in which provides a way to
easily identify it.
Executing Angular Application
Execute the application and check the output.
Open terminal in Visual Studio Code IDE by selecting View Menu -> Integrated Terminal.
Type the following command to run the application
Introduction to Templates
Templates separate the view layer from the rest of the framework.
You can change the view layer without breaking the application.
Templates in Angular represents a view and its role is to display data and change the data
whenever an event occurs
The default language for templates is HTML
Creating a template
Template can be defined in two ways:
Inline Template
External Template
Inline Template
You can create an inline template in a component class itself using the template property of the
@Component decorator.
app.component.ts
1. import { Component } from '@angular/core';
2. @Component({
3. selector: 'app-root',
4. template: `
5. <h1> Welcome </h1>
6. <h2> Course Name: {{ courseName }}</h2>
7. `,
8. styleUrls: ['./app.component.css']
9. })
10. export class AppComponent {
11. courseName = "Angular";
12. }
Line 4-8: You can even write HTML code inside the component using the template property. Use
backtick character (`) for multi-line strings.
Output:
External Template
By default, Angular CLI uses the external template.
It binds the external template with a component using templateUrl option.
Example
app.component.html
1. <h1> Welcome </h1>
2. <h2> Course Name: {{ courseName }}</h2>
app.component.ts
1. import { Component } from '@angular/core';
2. @Component({
3. selector: 'app-root',
4. templateUrl:'./app.component.html',
5. styleUrls: ['./app.component.css']
6. })
7. export class AppComponent {
8. courseName = "Angular";
9. }
Line 5: templateUrl property is used to bind an external template file with the component
Output:
Angular first evaluates the expression and returns the result as a string. The scope of a template
expression is a component instance.
That means, if you write {{ courseName }}, courseName should be the property of the
component to which this template is bound.
Template Statement
Template Statements are the statements that respond to a user event.
1. (event) = statement
Line 5-7: changeName is a method of AppComponent class where you are changing courseName
property value to "TypeScript"
app.component.html
1. <h1> Welcome </h1>
2. <h2> Course Name: {{ courseName }}</h2>
Line 3: changeName() method is bound to click event which will be invoked on click of a paragraph at
run time. This is called event binding.
Output:
When a user clicks on the paragraph, the course name will be changed to 'Typescript'.
Change Detection
How does Angular detect the changes and update the application at the respective places?
Angular uses its change detection mechanism to detect the changes and update the application at the
respective places. Angular applications run faster than Angular 1.x applications due to the
improved change detection mechanism.
What is the change detection mechanism, and how it helps to run Angular applications so fast?
Change Detection is a process in Angular that keeps views in sync with the models.
In Angular, the flow is unidirectional from top to bottom in a component tree. A change in a web
application can be caused by events, Ajax calls, and timers which are all asynchronous.
Who informs Angular about the changes?
Zones inform Angular about the changes in the application. It automatically detects all
asynchronous actions at run time in the application.
What happens when a change is detected? What does Angular do when a change is detected?
Angular runs a change detector algorithm on each component from top to bottom in the
component tree. This change detector algorithm is automatically generated at run time which will
check and update the changes at appropriate places in the component tree.
Angular is very fast though it goes through all components from top to bottom for every single
event as it generates VM-friendly code. Due to this, Angular can perform hundreds of thousands
of checks in a few milliseconds.
Structural Directives:
Now that you are familiar with the concept of templates, let us now understand directives in Angular.
Directives are used to change the behavior of components or elements. It can be used in the form
of HTML attributes.
You can create directives using classes attached with @Directive decorator which adds metadata
to the class.
Why Directives?
It modify the DOM elements
It creates reusable and independent code
It is used to create custom elements to implement the required functionality
Types of Directives
There are three types of directives available in Angular
Components
Components are directives with a template or view.
@Component decorator is actually @Directive with templates
Structural Directives
A Structural directive changes the DOM layout by adding and removing DOM elements.
1. *directive-name = expression
Line 5: Notice the use of ! with the property username. This is called adding definite assertion for
variables during declaration.
Definite assertion for variables
Angular CLI creates all new workspaces and projects with strict mode enabled. This leads to an improved
compile time check for properties. If any property is not initialized either by providing a default value or
by initializing within the constructor or if a parameter is not mapped to a property, the below error gets
generated:
property <<property>> has no initializer and is not definitely assigned in the constructor.
This is known as a definite assignment error because there is no definite assignment for that
property. The correct fix to this is to assign the property with a value in the constructor. An alternate is to
permit the property with no definite assignment by using the definite assignment assertion. You can do
that by adding ! to the property name.
userName in Line 5 of the above code doesn't have a value initialized and hence the use of !.
Line 7 -14: onSubmit method is invoked when the user clicks on the submit button in the template. This
method checks the username and password values entered are correct or not and accordingly assigns a
boolean value to isAuthenticated variable.
app.component.html
1. <div *ngIf="!submitted">
2. ...
3. <button (click)="onSubmit(username.value, password.value)">Login</button>
4. </div>
5.
6. <div *ngIf="submitted">
7. <div *ngIf="isAuthenticated; else failureMsg">
Line 7: Username will be displayed if 'isAuthenticated' property value is true otherwise it will be an error
message.
Output:
After entering the correct credentials and clicking on the Login button.
ngFor
ngFor directive is used to iterate over-collection of data i.e., arrays
Syntax:
1. *ngFor = "expression"
Example:
app.component.ts
1. ...
2. export class AppComponent {
3. courses: any[] = [
4. { id: 1, name: "TypeScript" },
5. { id: 2, name: "Angular" },
6. { id: 3, name: "Node JS" },
7. { id: 1, name: "TypeScript" }
8. ];
9. }
1. <ul>
2. <li *ngFor="let course of courses; let i = index">
3. {{i}} - {{ course.name }}
4. </li>
5. </ul>
Line 2: ngFor iterates over courses array and displays the value of name property of each course. It also
stores the index of each item in a variable called i
Line 3: {{ i }} displays the index of each course and course.name displays the name property value of
each course
Output:
ngSwitch
ngSwitch adds or removes DOM trees when their expressions match the switch expression. Its
syntax is comprised of two directives, an attribute directive, and a structural directive.
It is very similar to a switch statement in JavaScript and other programming languages.
Example:
app.component.ts
1. ...
2. export class AppComponent {
3. choice = 0;
4. nextChoice() {
5. this.choice++;
6. }
7. }
Line 2: ngSwitch takes the value and based upon the value inside the choice property, it executes
*ngSwitchCase. Paragraph elements will be added/removed from the DOM based on the value passed to
the switch case.
Output:
This will create two files under the src\app folder with names repeat.directive.ts and
repeat.directive.spec.ts (this is for testing). Now the app folder structure will look as shown below:
It also adds repeat directive to the root module i.e., app.module.ts to make it available to the entire
module as shown below in Line 7
app.module.ts
1. ...
2. import { RepeatDirective } from './repeat.directive';
3. @NgModule({
4. declarations: [
5. AppComponent,
6. RepeatDirective
7. ],
8. ...
9. })
10. export class AppModule { }
Line 3: Annotate the class with @Directive which represents the class as a directive and specify the
selector name inside brackets
Line 8: Create a constructor and inject two classes called TemplateRef which acquires <ng-template>
content and another class called ViewcontainerRef which access the HTML container to add or remove
elements from it
Line 10: Create a setter method for an appRepeat directive by attaching @Input() decorator which
specifies that this directive will receive value from the component. This method takes the number passed
to the appRepeat directive as an argument.
Line 12: As we need to render the elements based on the number passed to the appRepeat directive, run a
for loop in which pass the template reference to a createEmbeddedView method which renders the
elements into the DOM. This structural directive creates an embedded view from the Angular generated
<ng-template> and inserts that view in a view container.
app.component.html
1. <h3>Structural Directive</h3>
2. <p *appRepeat="5">I am being repeated...</p>
Line 2: appRepeat directive is applied to the paragraph element. It will render five paragraph elements
into the DOM.
Output:
Attribute Directives
Attribute directives change the appearance/behavior of a component/element.
Following are built-in attribute directives:
ngStyle
ngClass
ngStyle
This directive is used to modify a component/element’s style. You can use the following syntax to set a
single CSS style to the element which is also known as style binding.
1. [style.<cssproperty>] = "value"
Example:
app.component.ts
1. ...
2. export class AppComponent {
3. colorName = 'yellow';
4. color = 'red';
5. }
Line 3-4: colorName and color properties are initialized to default values
app.component.html
1. <div [style.background-color]="colorName" [style.color]="color">
2. Uses fixed yellow background
3. </div>
Line 1: style.background-color will set the background-color of the text to yellow and style.color
directive will set the color of the text to red.
Output:
If there are more than one CSS styles to apply, you can use ngStyle attribute.
Example:
app.component.ts
1. ...
2. export class AppComponent {
3. colorName = 'red';
4. fontWeight = 'bold';
5. borderStyle = '1px solid black';
6. }
Line3-5: Create three properties called colorName, fontWeight and borderStyle and initialize them with
some default values
app.component.html
1. <p [ngStyle]="{
2. color:colorName,
3. 'font-weight':fontWeight,
4. borderBottom: borderStyle
5. }">
6. Demo for attribute directive ngStyle
7. </p>
Line 1-5: ngStyle directive is used here to set multiple CSS styles for the given text
Output:
ngClass
It allows you to dynamically set and change the CSS classes for a given DOM element. Use the following
syntax to set a single CSS class to the element which is also known as class binding.
1. [class.<css_class_name>] = "property/value"
Example:
app.component.ts
1. ...
2. export class AppComponent {
3. isBordered = true;
4. }
Line 1: Bind the isBordered property with the CSS class bordered. Bordered CSS class will be applied
only if isBordered property evaluates to true.
In app.component.css, add the following CSS class
1. .bordered {
2. border: 1px dashed black;
3. background-color: #eee;
4. }
Output:
If you have more than one CSS classes to apply, then you can go for ngClass syntax.
Syntax:
1. [ngClass] = "{css_class_name1 : Boolean expression, css_class_name2: Boolean expression, ……}"
Example:
app.component.ts
1. ...
2. export class AppComponent {
3. isBordered = true;
4. isColor = true;
5. }
Line 3-4: Two Boolean properties called isBordered and isColor are initialized to true
app.component.html
1. <div [ngClass]="{bordered: isBordered, color: isColor}">
2. Border {{ isBordered ? "ON" : "OFF" }}
3. </div>
Line 1: Two CSS classes called bordered and color are applied to a div tag. Both the classes are binded
with isBordered and isColor properties where the CSS classes will be applied to div tag only if the
properties return true
Add the following CSS classes in app.component.css
1. .bordered {
2. border: 1px dashed black;
3. background-color: #eee;
4. }
5. .color {
6. color: blue;
7. }
Output:
Example:
Generate a directive called 'message' using the following command
1. D:\MyApp> ng generate directive message
This will create two files under the src\app folder with the names message.directive.ts and
message.directive.spec.ts (this is for testing). Now the app folder structure will look as shown below:
It also adds message directive to the root module i.e., app.module.ts to make it available to the entire
module as shown below
1. ...
2. import { MessageDirective } from './message.directive';
3. @NgModule({
4. declarations: [
5. AppComponent,
6. MessageDirective
7. ],
8. ...
9. })
10. export class AppModule { }
app.component.ts
1. import { Component } from '@angular/core';
2. @Component({
3. selector: 'app-root',
4. templateUrl: './app.component.html',
5. styleUrls: ['./app.component.css']
6. })
7. export class AppComponent {
8. myMessage="Hello, I am from attribute directive"
9. }
app.component.html
1. <h3>Attribute Directive</h3>
2. <p [appMessage]="myMessage">Click Here</p>
Line 2: Apply appMessage directive by assigning 'myMessage' property which will be sent to the
directive on click of the paragraph
Add the following CSS styles to the app.component.css file
1. h3 {
2. color: #369;
3. font-family: Arial, Helvetica, sans-serif;
4. font-size: 250%;
5. }
6. p{
7. color: #ff0080;
8. font-family: Arial, Helvetica, sans-serif;
9. font-size: 150%;
10. }
Output:
Data Binding
Data Binding is a mechanism where data in view and model are in sync. Users should be able to
see the same data in a view which the model contains.
As a developer, you need to bind the model data in a template such that the actual data reflects in
the view.
There are two types of data bindings based on the direction in which data flows.
One-way Data Binding
Two-way Data Binding
Here the component's imageUrl property is bound to the value to the image element's property src.
Interpolation can be used as an alternative to property binding. Property binding is mostly used when it is
required to set a non-string value.
Example:
First, create a folder called 'imgs' under the assets folder and copy any image into that folder.
app.component.ts
1. ...
2. export class AppComponent {
3. imgUrl: string = 'assets/imgs/logo.jpg';
4. }
Line 3: Create a property called imgUrl and initialize it to the image path
app.component.html
1. <img [src]='imgUrl' width=200 height=100>
Line 1: Bind imgUrl property with src property. This is called property binding
Note: this can also be written as:
Attribute Binding
Property binding will not work for a few elements/pure attributes like ARIA, SVG, and
COLSPAN. In such cases, you need to go for attribute binding.
Attribute binding can be used to bind a component property to the attribute directly
For example,
1. <td colspan = "{{ 2+3 }}">Hello</td>
The above example gives an error as colspan is not a property. Even if you use property
binding/interpolation, it will not work as it is a pure attribute. For such cases, use attribute
binding.
Attribute binding syntax starts with prefix attr. followed by a dot sign and the name of an
attribute. And then set the attribute value to an expression.
1. <td [attr.colspan] = "2+3">Hello</td>
Example:
app.component.ts
1. ...
2. export class AppComponent {
3. colspanValue: string ="2";
4. }
app.component.html
1. <table border=1>
2. <tr>
3. <td [attr.colspan]="colspanValue"> First </td>
4. <td>Second</td>
5. </tr>
6. <tr>
7. ...
8. </tr>
9. </table>
Line 3: attr.colspan will inform Angular that colspan is an attribute so that the given expression is
evaluated and assigned to it. This is called attribute binding.
Output:
Style Binding Style binding is used to set inline styles. Syntax starts with prefix style, followed by a
dot and the name of a CSS style property.
Syntax:
1. [style.styleproperty]
Example:
1. <button [style.color] = "isValid ? 'blue' : 'red' ">Hello</button>
Here button text color will be set to blue if the expression is true, otherwise red.
Some style bindings will have a unit extension.
Example:
1. <button [style.font-size.px] = "isValid ? 3 : 6">Hello</button>
Here text font size will be set to 3 px if the expression isValid is true, otherwise, it will be set to 6px.
The ngStyle directive is preferred when it is required to set multiple inline styles at the same time.
Event Binding
User actions such as entering text in input boxes, picking items from lists, button clicks may result in a
flow of data in the opposite direction: from an element to the component.
Event binding syntax consists of a target event with ( ) on the left of an equal sign and a template
statement on the right.
Example:
1. <button (click) ="onSubmit(username.value,password.value)">Login</button>
OR
1. <button on-click = "onSubmit(username.value,password.value)">Login</button>
Built-in Pipes:
Pipes provide a beautiful way of transforming the data inside templates, for the purpose of display.
Pipes in Angular
Pipes in Angular take an expression value as an input and transform it into the desired output before
displaying it to the user. It provides a clean and structured code as you can reuse the pipes throughout the
application, while declaring each pipe just once.
Syntax:
1. {{ expression | pipe }}
Example:
1. {{ "Angular" | uppercase }}
2. lowercase
3. titlecase
4. currency
5. date
6. percent
7. slice
8. decimal
uppercase
This pipe converts the template expression into uppercase.
Syntax:
1. {{ expression | uppercase }}
Example:
1. {{ "Laptop" | uppercase }}
Output:
LAPTOP
lowercase
This pipe converts the template expression into lowercase.
Syntax:
1. {{ expression | lowercase }}
Example:
1. {{ "LAPTOP" | lowercase }}
Output:
laptop
titlecase
This pipe converts the first character in each word of the given expression into a capital letter.
Syntax:
1. {{ expression | titlecase }}
Example:
1. {{ "product details" | titlecase }}
Output:
Product Details
Syntax:
1. pipename : parametervalue
1. pipename : parametervalue1:parametervalue2
Below are the built-in pipes present in Angular, which accept optional parameters using which the pipe's
output can be fine-tuned.
currency
This pipe displays a currency symbol before the expression. By default, it displays the currency symbol $
Syntax:
1. {{ expression | currency:currencyCode:symbol:digitInfo:locale }}
currencyCode is the code to display such as INR for the rupee, EUR for the euro, etc.
symbol is a Boolean value that represents whether to display currency symbol or code.
locale is used to set the format followed by a country/language. To use a locale, the locale needs to be
registered in the root module.
For Example,to set locale to French (fr), add the below statements in app.module.ts
1. import { registerLocaleData } from '@angular/common';
2. import localeFrench from '@angular/common/locales/fr';
3.
4. registerLocaleData(localeFrench);
Examples:
1. {{ 25000 | currency }} will display $25,000.00
2. {{ 25000 | currency:'CAD' }} will display CA$25,000.00
3. {{ 25000 | currency:'CAD':'code' }} will display CAD25,000.00
4. {{ 25000 | currency:'CAD':'symbol':'6.2-3'}} will display CA$025,000.00
date
This pipe can be used to display the date in the required format
Syntax:
1. {{ expression | date:format:timezone:locale }}
Timezone to be used for formatting. For example, ’+0430’ (4 hours, 30 minutes east of the Greenwich
meridian) If not specified, the local system timezone of the end-user's browser will be used.
locale is used to set the format followed by a country/language. To use a locale, register the locale in the
root module. For Example, to set locale to French (fr), add the below statements in app.module.ts
1. import { registerLocaleData } from '@angular/common';
2. import localeFrench from '@angular/common/locales/fr';
3. registerLocaleData(localeFrench);
Examples:
1. {{ "6/2/2017" | date }} will display Jun 2, 2017
2. {{ "6/2/2017, 11:30:45 AM" | date:'medium' }} will display Jun 2, 2017, 11:30:45 AM
3. {{ "6/2/2017, 11:30:45 AM" | date:'mmss' }} will display 3045
4. {{"1/31/2018, 11:05:04 AM" | date:'fullDate':'0':'fr'}} will display mercredi 31 janvier 2018
5. {{ 90000000 | date }} will display Jan 2, 1970 – date pipe will start from Jan 1, 1970 and based on the
given number of milliseconds, it displays the date
percent
Syntax:
1. {{ expression | percent:digitInfo:locale }}
locale is used to set the format followed by a country/language. To use a locale, register the locale in the
root module.
For Example, to set locale to French (fr), add the below statements in app.module.ts
1. import { registerLocaleData } from '@angular/common';
2. import localeFrench from '@angular/common/locales/fr';
3. registerLocaleData(localeFrench);
Examples:
1. {{ 0.1 | percent }} will display 10%
2. {{ 0.1 | percent:'2.2-3' }} will display 10.00%
3. {{ 0.1 | percent:'2.2-3': 'fr' }} will display 10.00 %
slice
This pipe can be used to extract a subset of elements or characters from an array or string respectively.
Syntax:
1. {{ expression | slice:start:end }}
The expression can be an array or string
start represents the starting position in an array or string to extract items. It can be a
positive integer which will extract from the given position till the end
negative integer which will extract the given number of items from the end
end represents the ending position in an array or string for extracting items. It can be
positive number that returns all items before the end index
negative number which returns all items before the end index from the end of the array or string
Examples:
1. {{ ['a','b','c','d']| slice:2}} will display c,d
number
This pipe can be used to format a number.
Syntax:
1. {{ expression | number:digitInfo }}
The expression should be numeric.
digitInfo is a string in the following format
{minIntegerDigits}.{minFractionDigits} - {maxFractionDigits}
minIntegerDigits is the minimum integer digits to display. The default value is 1.
minFractionDigits is the minimum number of digits to display after the fraction. The default value is 0.
maxFractionDigits is the maximum number of digits to display after fraction. The default value is 3.
Example:
1. {{ 25000 | number }} will display 25,000
2. {{ 25000 | number:'.3-5' }} will display 25,000.000
Nested Components
Nested component is a component that is loaded into another component
The component where another component is loaded onto is called a container component/parent
component.
The root component is loaded in the index.html page using its selector name. Similarly, to load
one component into a parent component, use the selector name of the component in the template
i.e., the HTML page of the container component
Example for creating multiple components and load one into another::
Problem Statement: Create an AppComponent which displays a button to view courses list on click of it
and another component called CoursesListComponent which displays courses list. When the user clicks
on the button in the AppComponent, it should load the CoursesList component within it to show the
courses list
Create a component called the coursesList using the following CLI command
1. D:\MyApp>ng generate component coursesList
This command will also add the CoursesList component to the root module.
app.module.ts
1. ...
2. import { CoursesListComponent } from './courses-list/courses-list.component';
3. @NgModule({
4. declarations: [
5. AppComponent,
6. CoursesListComponent
7. ],
8. ...
9. })
10. export class AppModule { }
Line 7: CoursesListComponent is added to the declarations property to make it available to all other
components in the module.
courses-list.component.ts
1. ...
2. export class CoursesListComponent {
3. courses = [
4. { courseId: 1, courseName: 'Node JS' },
5. { courseId: 2, courseName: 'Typescript' },
6. { courseId: 3, courseName: 'Angular' },
7. { courseId: 4, courseName: 'React JS' }
8. ];
9. }
Line 3-8: courses is an array of objects where each object has properties called courseId and courseName
courses-list.component.html
1. <table border="1">
2. ...
3. <tbody>
4. <tr *ngFor="let course of courses">
5. <td>{{course.courseId}}</td>
6. <td>{{course.courseName}}</td>
7. </tr>
8. </tbody>
9. </table>
Line 4-7: ngFor iterates over courses array and renders courseId and courseName values
app.component.html
1. ...
2. <button (click)="show=true">View Courses list</button><br/><br/>
3. <div *ngIf="show">
4. <app-courses-list></app-courses-list>
5. </div>
Line 2: click event is bounded to button which will initialize show property to true when it is clicked
Line 4: It loads the CoursesList component only if the show property value is true.
Note: Here AppComponent is called Parent component or container component as we are loading
another component in it.
CoursesList component is called child component as it is being loaded in another component.
Line 7: @Input() specifies that cName property will receive value from its container component
Line 9-13: for loop will iterate over courses array and it checks for the courseName validity. If it
matches, it adds that object to the course array
courses-list.component.html
1. <table border="1" *ngIf="course.length>0">
2. ...
3. <tbody>
4. <tr *ngFor="let c of course">
5. <td>{{c.courseId}}</td>
6. <td>{{c.courseName}}</td>
7. </tr>
8. </tbody>
9. </table>
Line 4-7: ngFor iterates on course array and displays courseId and courseName properties in a table
Add the following code in app.component.html
1. <h2>Course Details</h2>
2. Select a course to view
3. <select #course (change)="name = course.value">
4. <option value="Node JS">Node JS</option>
5. <option value="Typescript">Typescript</option>
6. <option value="Angular">Angular</option>
7. <option value="React JS">React JS</option></select>
8. <br /><br />
9. <app-courses-list [cName]="name"></app-courses-list>
Line 2-7: It displays a drop-down to select a course to display its details. When the user selects a value, it
assigns the selected value to the name property.
Line 9: This will load CoursesListComponent and passes the name property value to the cName property
of CoursesListComponent class.
Best Practices - Coding Style Rules
Place @Input decorator on the same line as the property to make it shorter and more readable.
courses-list.component.ts
1. ...
2. export class CoursesListComponent {
3. @Output() onRegister = new EventEmitter<string>();
4. ...
5. register(courseName: string) {
6. this.onRegister.emit(courseName);
7. }
8. }
Line 3: Create a property called onRegister of type EventEmitter and attach @Output decorator which
makes the property to send the data from child to parent
Line 7: this line emits the courseName value i.e, send the courseName value back to parent component.
courses-list.component.html
1. <table border="1">
2. ...
3. <tbody>
4. <tr *ngFor="let course of courses">
5. <td>{{course.courseId}}</td>
6. <td>{{course.courseName}}</td>
7. <td><button (click)="register(course.courseName)">Register</button></td>
8. </tr>
9. </tbody>
10. </table>
Line 7: When the user clicks this button, it invokes the register method by passing courseName value.
app.component.html
1. <h2> Courses List </h2>
2. <app-courses-list (OnRegister)="courseReg($event)"></app-courses-list>
3. <br/><br/>
4. <div *ngIf="message">{{message}}</div>
Line 3: Binds onRegister event with courseReg method of the parent component. When
CoursesListComponent emits the value, onRegister event is triggered and it invokes courseReg method.
$event holds the value emitted by CoursesListComponent
Line 6: This renders the message property value which holds the value emitted
app.component.ts
1. ...
2. export class AppComponent {
3. message!: string;
4. courseReg(courseName: string) {
5. this.message = `Your registration for ${courseName} is successful`;
6. }
7. }
Syntax:
1. import { Component, OnInit, DoCheck, AfterContentInit, AfterContentChecked,
2. AfterViewInit, AfterViewChecked, OnDestroy } from '@angular/core';
3. ...
4. export class AppComponent implements OnInit, DoCheck, AfterContentInit, AfterContentChecked,
5. AfterViewInit, AfterViewChecked, OnDestroy {
6. ngOnInit() { }
7. ngDoCheck() { }
8. ngAfterContentInit() { }
9. ngAfterContentChecked() { }
10. ngAfterViewInit() { }
11. ngAfterViewChecked() { }
12. ngOnDestroy() { }
13. }
Example:
app.component.ts
1. import {
2. Component, OnInit, DoCheck, AfterContentInit, AfterContentChecked,
3. AfterViewInit, AfterViewChecked,
4. OnDestroy
5. } from '@angular/core';
6. @Component({
7. selector: 'app-root',
8. styleUrls: ['./app.component.css'],
9. templateUrl: './app.component.html'
10. })
11. export class AppComponent implements OnInit, DoCheck,
12. AfterContentInit, AfterContentChecked,
13. AfterViewInit, AfterViewChecked,
14. OnDestroy {
15. data = 'Angular';
16. ngOnInit() {
17. console.log('Init');
18. }
19. ngDoCheck(): void {
20. console.log('Change detected');
21. }
22. ngAfterContentInit(): void {
23. console.log('After content init');
24. }
25. ngAfterContentChecked(): void {
26. console.log('After content checked');
27. }
28. ngAfterViewInit(): void {
29. console.log('After view init');
30. }
31. ngAfterViewChecked(): void {
32. console.log('After view checked');
33. }
34. ngOnDestroy(): void {
35. console.log('Destroy');
36. }
37. }
and from later whenever a change happens Angular invokes ngDoCheck, ngAfterContentChecked and
ngAfterViewChecked methods
app.component.html
1. <div>
2. <h1>I'm a container component</h1>
3. <input type="text" [(ngModel)]='data'>
4. <app-child [title]='data'></app-child>
5. </div>
child.component.ts
1. ...
2. export class ChildComponent implements OnChanges {
3. @Input() title: string = 'I\'m a nested component';
4. ngOnChanges(changes: any): void {
5. console.log('changes in child:' + JSON.stringify(changes));
6. }
7. }
Line 4: title is an input property that receives value from App component
Line 6: Override ngOnChanges method which gets invoked whenever input property changes its value.
Note: Whenever the input property called title changes its value, Angular invokes ngOnChanges()
method which takes the changes as a JSON object. The 'changes' parameter will have the previous value
and the current value of the input property
child.component.html
1. <h2>Child Component</h2>
2. <h2>{{title}}</h2>
Output:
Browser Console:
Forms in Angular
Forms are crucial part of web applications. Forms enable users to provide data input into the application
in contexts like performing user registration, user sign-in, updating profile, entering sensitive information
like payment information and for performing other data-entry based tasks.
Forms in Angular:
Angular has two different approaches in dealing with forms: reactive forms and template-driven forms.
Both reactive forms and template-driven forms:
can capture user-provided data,
can capture user input events,
can validate the user input, etc.
have their own approaches of processing and managing the form data:
o In template-driven forms, you will create the form completely in the template and need to rely on
directives to create and manipulate the underlying form object model. Since the template-driven
forms do not scale that well, they are more suitable only when you want to add a simple small
form to the application. For example: a signup form.
o In reactive forms, you can control the form completely from the component class and hence you
will get direct, explicit access to the underlying forms object model. Hence, reactive forms are also
known as 'model-driven forms'. As reactive forms are more robust and scalable, they are more
suitable for creating all kind of forms in an application, irrespective of the size of form.
State of forms and form controls in Angular:
Angular automatically tracks the changes happening to the form and form controls as and when user provides input
and thereby controls the state and the validity of the form/form controls. Angular does this by associating
respective keywords automatically for the forms/form controls depending on the context. The below table
describes the details:
Angular also has the following built-in CSS classes which get auto-applied depending on the state. Code can be
written inside these CSS classes which can change the appearance of the control suitably as per context.
Due to the advantages of Reactive forms, in most Angular applications, Reactive Forms Approach is
chosen when creating forms.
Step 3:
Add the below to app.component.html
1. <app-registration-form></app-registration-form>
9. ]
10. ...
Note: When angular.json is modified, restart the server to see the changes reflected.
Step 5:
Include the below code to registration-form.component.css
1. .ng-valid[required] {
2. border-left: 5px solid #42A948; /* green */
3. }
4. .ng-invalid:not(form) {
5. border-left: 5px solid #a94442; /* red */
6. }
Line 1-3: ng-valid CSS class changes left border of the textbox to green if form control has valid input
Line 5-7: ng-invalid CSS class changes left border of the textbox to red if form control has invalid data
Line 2: Import FormBuilder class to create a reactive form. Also, import FormGroup class to create a
group of form controls and Validators for validation
Line 17: formBuilder.group() method creates a FormGroup. It takes an object whose keys are
FormControl names and values are their definitions
Line 18-24: Create form controls such as firstName, lastName, and address as a subgroup with fields
street, zip, and city. These fields are form controls.
registration-form.component.html
1. <div class="container">
2. <h1>Registration Form</h1>
3. <form [formGroup]="registerForm">
4. <div class="form-group">
5. <label>First Name</label>
6. <input type="text" class="form-control" formControlName="firstName">
7. <div *ngIf="registerForm.controls['firstName'].errors" class="alert alert-danger">
8. Firstname field is invalid.
9. <p *ngIf="registerForm.controls['firstName'].errors?.['required']">
10. This field is required!
11. </p>
12. </div>
13. </div>
14. <div class="form-group">
15. <label>Last Name</label>
16. <input type="text" class="form-control" formControlName="lastName">
17. <div *ngIf="registerForm.controls['lastName'].errors" class="alert alert-danger">
18. Lastname field is invalid.
19. <p *ngIf="registerForm.controls['lastName'].errors?.['required']">
20. This field is required!
21. </p>
22. </div>
23. </div>
24. <div class="form-group">
25. <fieldset formGroupName="address">
26. <legend>Address:</legend>
27. <label>Street</label>
28. <input type="text" class="form-control" formControlName="street">
29. <label>Zip</label>
30. <input type="text" class="form-control" formControlName="zip">
31. <label>City</label>
32. <input type="text" class="form-control" formControlName="city">
33. </fieldset>
34. </div>
35. <button type="submit" class="btn btn-primary" (click)="submitted=true">Submit</button>
36. </form>
37. <br/>
38. <div [hidden]="!submitted">
39. <h3> Employee Details </h3>
40. <p>First Name: {{ registerForm.get('firstName')?.value }} </p>
41. <p> Last Name: {{ registerForm.get('lastName')?.value }} </p>
42. <p> Street: {{ registerForm.get('address.street')?.value }}</p>
43. <p> Zip: {{ registerForm.get('address.zip')?.value }} </p>
44. <p> City: {{ registerForm.get('address.city')?.value }}</p>
45. </div>
46. </div>
Line 3: formGroup is a directive that binds HTML form with the FormGroup property created inside a
component class. A FormGroup has been created in component with the name registerForm. Here form
tag is bound with FormGroup name called registerForm
Line 6, 16: Two text boxes for first name and last name are bound with the form controls created in the
component using formControlName directive
Line 7-13: A validation error message is displayed when the firstName is modified and has validation
errors.
Line 17-22: A validation error message is displayed when the lastName is modified and has validation
errors.
Line 35: When the submit button is clicked, it initializes the submitted property value to true
Line 38: div tag will be hidden if the form is not submitted
Line 39-44: Using the get() method of FormGroup, each FormControl value is fetched and rendered.
Save all the files and observe the output:
For implementing the for custom validation, add a separate function which checks for standard email
pattern inside registration-form.component.ts as shown below:
10. submitted!:boolean;
11. constructor(private formBuilder: FormBuilder) { }
12. ngOnInit() {
13. this.registerForm = this.formBuilder.group({
14. firstName: ['', Validators.required],
15. lastName: ['', Validators.required],
16. address: this.formBuilder.group({
17. street: [],
18. zip: [],
19. city: []
20. }),
21. email: ['', validateEmail]
22. });
23. }
24. }
25. function validateEmail(c: FormControl): any {
26. let EMAIL_REGEXP = /^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/;
27. return EMAIL_REGEXP.test(c.value) ? null : {
28. emailInvalid: {
29. message: "Invalid Format!"
30. }
31. };
32. }
Line 30-37: In this function, a regular expression pattern is taken for email and the input value of the
form control is tested against the mentioned pattern. If the pattern matches, it means the entered input is
valid and hence, the validation function returns null. Otherwise, the function returns an object with name
'emailInvalid' with one property called 'message' set to appropriate string message.
Line 21: Binds the required validator and the custom validator named validateEmail to the email field.
Tracking the Custom Validators in the Reactive Form's template (Angular v13)
For tracking the custom validators in the Reactive Form's template (Angular v13), add HTML controls
for the email field in the registration-form.component.html file as shown below:
1. <div class="container">
2. <h1>Registration Form</h1>
3. <form [formGroup]="registerForm">
4. <div class="form-group">
5. <label>First Name</label>
6. <input type="text" class="form-control" formControlName="firstName">
7. <div *ngIf="registerForm.controls['firstName'].errors" class="alert alert-danger">
8. Firstname field is invalid.
9. <p *ngIf="registerForm.controls['firstName'].errors?.['required']">
10. This field is required!
11. </p>
12. </div>
13. </div>
14. <div class="form-group">
15. <label>Last Name</label>
16. <input type="text" class="form-control" formControlName="lastName">
17. <div *ngIf="registerForm.controls['lastName'].errors" class="alert alert-danger">
18. Lastname field is invalid.
19. <p *ngIf="registerForm.controls['lastName'].errors?.['required']">
20. This field is required!
21. </p>
22. </div>
23. </div>
24. <div class="form-group">
25. <fieldset formGroupName="address">
26. <legend>Address:</legend>
27. <label>Street</label>
28. <input type="text" class="form-control" formControlName="street">
29. <label>Zip</label>
30. <input type="text" class="form-control" formControlName="zip">
31. <label>City</label>
32. <input type="text" class="form-control" formControlName="city">
33. </fieldset>
34. </div>
35. <div class="form-group">
36. <label>Email</label>
37. <input type="text" class="form-control" formControlName="email" />
38. <div *ngIf="registerForm.controls['email'].errors" class="alert alert-danger">
39. Email field is invalid.
40. <p *ngIf="registerForm.controls['email'].errors?.['required']">
41. This field is required!
42. </p>
43. <p *ngIf="registerForm.controls['email'].errors?.['emailInvalid']">
44. {{ registerForm.controls['email'].errors?.['emailInvalid'].message }}
45. </p>
46. </div>
47. </div>
48. <button type="submit" class="btn btn-primary" (click)="submitted=true">Submit</button>
49. </form>
50. <br/>
51. <div [hidden]="!submitted">
52. <h3> Employee Details </h3>
53. <p>First Name: {{ registerForm.get('firstName')?.value }} </p>
54. <p> Last Name: {{ registerForm.get('lastName')?.value }} </p>
55. <p> Street: {{ registerForm.get('address.street')?.value }}</p>
56. <p> Zip: {{ registerForm.get('address.zip')?.value }} </p>
57. <p> City: {{ registerForm.get('address.city')?.value }}</p>
58. <p>Email: {{ registerForm.get('email')?.value }}</p>
59. </div>
60. </div>
Line 44: Displays error message if email validation fails. errors object holds the error messages of all
form controls
Output:
Dependency Injection
Dependency Injection (DI) is a mechanism where the required resources will be injected into the code
automatically.
Angular comes with an in-built dependency injection subsystem.
allows the developer to ask for the dependencies from Angular. There is no need for the developer
to explicitly create/instantiate them.
Services Basics:
A service in Angular is a class that contains some functionality that can be reused across the
application. A service is a singleton object. Angular services are a mechanism of abstracting shared
code and functionality throughout the application.
Angular Services come as objects which are wired together using dependency injection.
Angular provides a few inbuilt services also can create custom services.
Why Services?
Services can be used to:
share the code across components of an application.
make HTTP requests.
Creating a Service
To create a service class, use the following command:
1. ng generate service book
Line 4: providedIn property registers BookService at the root level (app module).
When the BookService is provided at the root level, Angular creates a singleton instance of the service
class and injects the same instance into any class that uses this service class. In addition, Angular also
optimizes the application if registered through providedIn property by removing the service class if none
of the components use it.
2. Services can also be provided across the application by registering it using the providers property in
the @Ngmodule decorator of any module.
1. @NgModule({
2. imports: [BrowserModule],
3. declarations: [AppComponent, BookComponent],
4. providers: [BookService],
5. bootstrap: [AppComponent]
6. })
Line 4: When the service class is added in the providers property of the root module, all the directives
and components will have access to the same instance of the service.
3. There is also a way to limit the scope of the service class by registering it in the providers' property
inside the @Component decorator. Providers in component decorator and module decorator are
independent. Providing a service class inside a component creates a separate instance for that component
and its nested components.
1. import { BookService } from './book/book.service';
2. @Component({
3. selector: 'app-root',
4. styleUrls: ['./app.component.css'],
5. templateUrl: './app.component.html',
6. providers:[BookService]
7. })
Injecting a Service
The only way to inject a service into a component/directive or any other class is through a constructor.
Add a constructor in a component class with service class as an argument as shown below:
1. constructor(private bookService: BookService){ }
BookService will then be injected into the component through constructor injection by the framework.
Best Practices - Coding Style Rules
Use services for sharing data and functionality.
Use a service with a single responsibility to make the testing easier.
Line 1-4: Create a Book class with two properties id and name to store book id and book name
Create a file with the name books-data.ts under the book folder and add the following code.
1. import { Book } from './book';
Line 3-14: Books is an array of type Book class which holds books objects where each object has id and
name properties.
Create a service called BookService under the book folder using the following CLI command
1. D:\MyApp\src\app\book>ng generate service book
This will create two files called book.service.ts and book.service.spec.ts as shown below
Line 5-7: @Injectable() decorator makes the class as a service which can be injected into components of
an application
Line 10-12: getBooks() method returns Books data.
Add the following code in the book.component.ts file
1. ...
2. import { BookService } from './book.service';
3. import { Book } from './book';
4.
5. ...
6. export class BookComponent implements OnInit {
7. books!: Book[];
8. constructor(private bookService: BookService) { }
9. getBooks() {
10. this.books = this.bookService.getBooks();
11. }
12. ngOnInit() {
13. this.getBooks();
14. }
15. }
Add the following code in book.component.css which has styles for books
1. .books {
2. margin: 0 0 2em 0;
3. list-style-type: none;
4. padding: 0;
5. width: 15em;
6. }
7. .books li {
8. cursor: pointer;
9. position: relative;
10. left: 0;
11. background-color: #EEE;
12. margin: .5em;
13. padding: .3em 0;
14. height: 1.6em;
15. border-radius: 4px;
16. }
17. .books li:hover {
18. color: #607D8B;
19. background-color: #DDD;
20. left: .1em;
21. }
22. .books .badge {
23. display: inline-block;
24. font-size: small;
25. color: white;
26. padding: 0.8em 0.7em 0 0.7em;
27. background-color: #607D8B;
28. line-height: 1em;
29. position: relative;
30. left: -1px;
31. top: -4px;
32. height: 1.8em;
33. margin-right: .8em;
34. border-radius: 4px 0 0 4px;
35. }
Output:
RxJS Observables
Reactive Extensions for JavaScript (RxJS) is a third-party library used by the Angular team.
RxJS is a reactive streams library used to work with asynchronous streams of data.
Observables, in RxJS, are used to represent asynchronous streams of data. Observables are a more
advanced version of Promises in JavaScript
Why RxJS Observables?
Angular team has recommended Observables for asynchronous calls because of the following reasons:
1. Promises emit a single value whereas observables (streams) emit many values
2. Observables can be cancellable where Promises are not cancellable. If an HTTP response is not
required, observables allow us to cancel the subscription whereas promises execute either success
or failure callback even if the results are not required.
3. Observables support functional operators such as map, filter, reduce, etc.,
Line 22: Observable has another method called subscribe which listens to the data coming through the
stream. Subscribe() method has three parameters. The first parameter is a success callback which will be
invoked upon receiving successful data from the stream. The second parameter is an error callback which
will be invoked when Observable returns an error and the third parameter is a complete callback which
will be invoked upon successful streaming of values from Observable i.e., once complete() is invoked.
After which the successful response, the data is pushed to the local array called myArray, if any error
occurs, a Boolean value called true is stored in the errors variable and upon complete() will assign a
Boolean value true in a finished variable.
app.component.html
1. <b> Using Observables!</b>
2. <h6 style="margin-bottom: 0">VALUES:</h6>
3. <div *ngFor="let value of myArray">{{ value }}</div>
4. <div style="margin-bottom: 0">ERRORS: {{ errors }}</div>
5. <div style="margin-bottom: 0">FINISHED: {{ finished }}</div>
6. <button style="margin-top: 2rem" (click)="fetchData()">Fetch Data</button>
Line 4: ngFor loop is iterated on myArray which will display the values on the page
Line 6: {{ errors }} will render the value of errors property if any
Line 8: Displays finished property value when complete() method of Observable is executed
Line 10: Button click event is bound with fetchData() method which is invoked and creates an observable
with a stream of numeric values
Output:
Most front-end applications communicate with backend services using HTTP Protocol
While making calls to an external server, the users must continue to be able to interact with the
page. That is, the page should not freeze until the HTTP request returns from the external server.
So, all HTTP requests are asynchronous.
HttpClient from @angular/common/http to communicate must be used with backend services.
Additional benefits of HttpClient include testability features, typed request and response objects,
request and response interception, Observable APIs, and streamlined error handling.
HttpClientModule must be imported from @angular/common/http in the module class to make
HTTP service available to the entire module. Import HttpClient service class into a component’s
constructor. HTTP methods like get, post, put, and delete are made used off.
JSON is the default response type for HttpClient
statements once a response is ready which is mostly used for debugging purposes
and catchError operator is used to handling the errors.
Line 18: handleError is an error-handling method that throws the error message back to the component
Error handling
What happens if the request fails on the server, or if a poor network connection prevents it from
even reaching the server?
There are two types of errors that can occur. The server might reject the request, returning an
HTTP response with a status code such as 404 or 500. These are error responses.
Or something could go wrong on the client-side such as a network error that prevents the request
from completing successfully or an exception thrown in an RxJS operator. These errors produce
JavaScript ErrorEvent objects.
HttpClient captures both kinds of errors in its HttpErrorResponse and it can be inspected for
the response to find out what really happened.
There must be error inspection, interpretation, and resolution in service not in the component.
Line 5: HttpErrorResponse module class should be imported to understand the nature of the error thrown
Line 18-21: An instance of Error object will be thrown if any network or client-side error is thrown
Line 22-26: Handling of errors due to unsuccessful response codes from the backend
Line 7: Inject the BookService class into the component class through the constructor
Line 9-14: Invokes the service class method getBooks() which makes an HTTP call to the books.json file.
The getBooks() of the service class returns an Observable.
An observable in Angular begins to publish values only when someone has subscribed to it. To retrieve
the value contained in the Observable returned by getBooks() of service, subscribe to the observable by
calling the subscribe() method and pass an observer object which can listen to the three types of
notifications that an observable can send: next, error and complete.
Notification
Details
Type
Required.
Next
Handler for each delivered value. Gets called zero or more times once execution starts.
Optional.
Error Handler for handling an error notification. If an error occurs, it stops the execution of the
observable instance.
Optional.
complete Handler for handling the execution-completion notification. If any values have been
delayed, those can be still delivered to the next handler even after execution is complete.
book.component.html
1. ...
2. <ul class="books">
3. <li *ngFor="let book of books">
4. <span class="badge">{{book.id}}</span> {{book.name}}
5. </li>
6. </ul>
7. <div class="error" *ngIf="errorMessage">{{errorMessage}}</div>
Output:
Line 16-20: Makes an asynchronous call (ajax call) by using the post() method of HttpClient class. This
method makes an asynchronous call to the server URL and sends the data along with the headers.
HttpClient receives the JSON response as of type object. The Pipe function lets you define a comma-
separated sequence of operators. Here, a sequence of observables is defined by listing operators as
arguments to pipe function instead of dot operator chaining. catchError operator is used to handle the
errors.
Line 19: handleError is an error-handling method that throws the error message back to the component
Modify the code in the book.component.ts file as shown below
1. ...
Line 7: Inject the BookService class into the component class through the constructor
Line 15-19: Invokes the service class method addBook() which makes an HTTP call to server URL and
the Observable containing the response is returned. You can subscribe to the observable and use the 'next'
callback to handle the successfully returned value, as needed.
Making a PUT request
HttpClient.put() method completely replaces the resource with the updated data. It is like POST requests
except for updating an existing resource.
Add updateBook() method to BookService class in book.service.ts file as shown below:
1. import { Injectable } from '@angular/core';
2. import { HttpClient } from '@angular/common/http';
3. import { Observable, throwError } from 'rxjs';
4. import { catchError, tap } from 'rxjs/operators';
5. import { Book } from './book';
6. @Injectable({
7. providedIn: 'root'
8. })
9. export class BookService {
10. constructor(private http: HttpClient) { }
11. ...
12. updateBook(book: Book): Observable<any> {
13. const options = new HttpHeaders({ 'Content-Type': 'application/json' });
14. return this.http.put<any>('http://localhost:3020/update', book, { headers: options }).pipe(
15. tap((_: any) => console.log(`updated hero id=${book.id}`)),
16. catchError(this.handleError)
17. );
18. }
19. ...
20. }
Line 16-20: Makes an asynchronous call (ajax call) by using the put() method of HttpClient class. This
method makes an asynchronous call to the server URL and sends the data along with the headers as that
of POST requests. HttpClient receives the JSON response as of type object. Pipe function defines a
comma-separated sequence of operators. Here a sequence of observables is defined by listing operators as
arguments to pipe function instead of dot operator chaining. tap operator is to execute some statements
once a response is ready which is mostly used for debugging purposes and catchError operator is used
to handle the errors.
Line 20: handleError is an error-handling method that throws the error message back to the component.
Line 7: Inject the BookService class into the component class through the constructor.
Line 20-24: Invokes the service class method updateBook() which makes an HTTP call to server URL
and the Observable containing the response is returned. You can subscribe to the observable and use the
'next' callback to handle the successfully returned value, as needed.
Making a DELETE request :
HttpClient.delete() method deletes the resource by passing the bookId parameter in the request URL.
Add deleteBook() method to BookService class in book.service.ts file as shown below
1. import { Injectable } from '@angular/core';
Line 18-21: Makes an asynchronous call (ajax call) by using delete() method of HttpClient
class. HttpClient receives the JSON response as of type object. Pipe function defines a comma-separated
sequence of operators. catchError operator is used to handle the errors.
Line 21: handleError is an error-handling method that throws the error message back to the component.
Modify the code in the book.component.ts file as shown below
1. ...
2. export class BookComponent implements OnInit {
3. books!: Book[];
4. errorMessage!: string;
5. constructor(private bookService: BookService) { }
6. getBooks() {
7. this.bookService.getBooks().subscribe({
8. next: books => this.books = books,
9. error:error => this.errorMessage = <any>error
10. })
11. }
12. addBook(bookId: string, name: string): void {
13. let id=parseInt(bookId)
14. this.bookService.addBook({id, name })
15. .subscribe({next:(book: any) => this.books.push(book)});
16. }
17. updateBook(bookId: string, name: string): void {
18. let id=parseInt(bookId)
19. this.bookService.updateBook({ id, name })
20. .subscribe({next:(book: any) => this.books = book});
21. }
22. deleteBook(bookId: string): void {
23. let id=parseInt(bookId)
24. this.bookService.deleteBook(id)
25. .subscribe({next:(book: any) => this.books = book});
26. }
27. ngOnInit(): void {
28. this.getBooks();
29. }
30. }
Line 7: Inject the BookService class into the component class through the constructor
Line 25-29: Invokes the service class method deleteBook() which makes an HTTP call to server URL
and the Observable containing the response is returned. You can subscribe to the observable and use the
'next' callback to handle the successfully returned value, as needed.
Some of the best practices of server communication are:
Always use services to talk to the server as components' responsibility is only to present the data.
Data services should be responsible for asynchronous calls, local storage, or any other data
operations as it will become easier to test the data calls.
The information like headers, HTTP methods, caching, error handling, etc., are irrelevant to
components and they need to be in a service class. A service encapsulates these details and it will be
easier to test the components with mock service implementations.
Angular can also be used to connect to different services written in different technologies/languages. For
example, we can make a call from Angular to Node/Express or Java or Mainframe or .Net services, etc.
Routing Basics:
Configuring Routing in an Angular application
Routing means navigation between multiple views on a single page.
Routing allows to express some aspects of the application's state in the URL. The full application can be
built without changing the URL.
Why Routing?
Routing allows to:
Navigate between the views
Create modular applications
Configuring Router
Angular uses Component Router to implement routing
A <base> tag must be added to the head tag in the HTML page to tell the router where to start with.
1. <base href="/">
Angular component router belongs to @angular/router module. To make use of routing, Routes,
RouterModule classes must be imported.
Configuration should be done for the routes and the router will look for a corresponding route
when a browser URL is changed.
Routes is an array that contains all the route configurations. Then, this array should be passed to
the RouterModule.forRoot() function in the application bootstrapping function
Example:
Consider the example used in the services concept.
Add the following in the app-routing.module.ts which is created under the app folder
1. import { NgModule } from '@angular/core';
2. import { RouterModule, Routes } from '@angular/router';
3. import { BookComponent } from './book/book.component';
4. import { DashboardComponent } from './dashboard/dashboard.component';
5. import { BookDetailComponent } from './book-detail/book-detail.component';
6. import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
7. const appRoutes: Routes = [
8. { path: 'dashboard', component: DashboardComponent },
9. { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
10. { path: 'books', component: BookComponent },
11. { path: 'detail/:id', component: BookDetailComponent },
12. { path: '**', component: PageNotFoundComponent },
13. ];
14. @NgModule({
15. imports: [
16. RouterModule.forRoot(appRoutes)
17. ],
18. exports: [
19. RouterModule
20. ]
21. })
22. export class AppRoutingModule { }
Router Links:
Navigate using Angular Router
After configuring the routes, the next step is to decide how to navigate. Navigation will happen based on
user actions like clicking a hyperlink, clicking on a button, etc. Hence, there is hyperlink based
navigation and programmatical navigation.
Hyperlink based navigation
RouterLink directive can be used with the anchor tag for using hyperlink based navigation in Angular.
Have a look at code shown below:
app.component.ts
1. import { Component } from '@angular/core';
2. @Component({
3. selector: 'app-root',
4. styleUrls: ['./app.component.css'],
5. templateUrl: './app.component.html'
6. })
7. export class AppComponent {
8. title = 'Tour of Books';
9. }
app.component.html
1. <h1>{{title}}</h1>
2. <nav>
3. <a [routerLink]='["/dashboard"]' routerLinkActive="active">Dashboard</a>
4. <a [routerLink]='["/books"]' routerLinkActive="active">Books</a>
5. </nav>
6. <router-outlet></router-outlet>
Line 3-4: Create hyperlinks and a routerLink directive and specify the paths to navigate. Here, if a user
clicks on the Dashboard, it will navigate to /dashboard. routerLinkActive applies the given CSS class to
the link when it is clicked to make it look like an active link(active is a CSS class defined in
app.component.css which changes the link color to blue in this case).
Line 6: <router-outlet> is the place where the output of the component associated with the given path will
be displayed. For example, if the user click on Books, it will navigate to /books which will execute
BooksComponent class as mentioned in the configuration details and the output will be displayed in the
router-outlet class.
Programmatical navigation
To navigate programmatically, use the navigate() method of the Router class. Inject the router class into
the component and invoke the navigate method as shown below
1. this.router.navigate([url, parameters])
URL is the route path to which we want to navigate
Parameters are the route values passed along with the URL
16. this.bookService.getBooks()
17. .subscribe({next:books => this.books = books.slice(1, 5)});
18. }
19. gotoDetail(book: Book): void {
20. this.router.navigate(['/detail', book.id]);
21. }
22. }
24. goBack() {
25. window.history.back();
26. }
27. }
When a specific book is clicked, it renders the BookDetailComponent which displays details of that
particular book, as is shown below:
When the Books link is clicked, it navigates to BooksComponent which displays the list of all the books,
as is shown below:
11. <input
12. type="password"
13. formControlName="password"
14. style="position: relative; left: 10px"
15. />
16. </p>
17. <p><button type="submit">Submit</button></p>
18. </form>
19. </div>
Line 2: div tag will render error message for incorrect credentials
Line 7-18: A reactive form with two fields username and password is displayed
33. }});
34. }
35. }
Line 25: onFormSubmit() method is invoked when the submit button is clicked in Login Form
Line 26-27: Fetching username and password values from the form
Line 28: Invoking isUserAuthenticated method of LoginService class which will check for the validity of
username and password values and returns a Boolean value
Line 31-35: If the response is true, it will navigate to BooksComponent else assigns an error message to
invalidCredentialMsg property
Add the following code to the user.ts file inside Login folder.
1. export class User {
2. constructor(public userId: number, public username: string, public password: string) { }
3. }
Line 1-3: A User model class with three properties userId, username, and password is created
Add the following code to the login.service.ts file present inside login folder.
1. import { Injectable } from '@angular/core';
2. import { Observable, of } from 'rxjs';
3. import { map } from 'rxjs/operators';
4. import { User } from './user';
5. const USERS = [
6. new User(1, 'user1', 'user1'),
7. new User(2, 'user2', 'user2')
8. ];
9. const usersObservable = of(USERS);
10. @Injectable({
11. providedIn: 'root'
12. })
13. export class LoginService {
14. private isloggedIn = false;
15. getAllUsers(): Observable<User[]> {
16. return usersObservable;
17. }
18. isUserAuthenticated(username: string, password: string): Observable<boolean> {
19. return this.getAllUsers().pipe(
20. map(users => {
21. const Authenticateduser = users.find(user => (user.username === username) && (user.password ===
password));
22. if (Authenticateduser) {
23. this.isloggedIn = true;
24. } else {
25. this.isloggedIn = false;
26. }
27. return this.isloggedIn;
28. })
29. );
30. }
31. isUserLoggedIn(): boolean {
32. return this.isloggedIn;
33. }
34. }
Create another service class called login-guard.service inside login folder and add the following code:
1. import { Injectable } from '@angular/core';
2. import { CanActivate, Router } from '@angular/router';
3. import { LoginService } from './login.service';
4. @Injectable({
5. providedIn: 'root'
6. })
7. export class LoginGuardService implements CanActivate {
8. constructor(private loginService: LoginService, private router: Router) { }
9. canActivate(): boolean {
10. if (this.loginService.isUserLoggedIn()) {
11. return true;
12. }
13. this.router.navigate(['/login']);
14. return false;
15. }
16. }
4. {path: 'login',component:LoginComponent},
5. { path: 'books', component: BookComponent, canActivate:[LoginGuardService] },
6. { path: 'dashboard', component: DashboardComponent},
7. { path: 'detail/:id', component: BookDetailComponent},
8. { path: '**', component: PageNotFoundComponent },
9. ];
10. ...
Asynchronous Routing
When an Angular application has a lot of components, it will increase the size of the application.
In such cases, the application takes a lot of time to load.
To overcome this problem, asynchronous routing is preferred, i.e, modules must be loaded lazily
only when they are required instead of loading them at the beginning of the execution
Lazy Loading has the following benefits:
Modules are loaded only when the user requests for it
Load time can be speeded up for users who will be visiting only certain areas of the application
Lazy Loading Route Configuration: To apply lazy loading on modules, create a separate routing
configuration file for that module and map an empty path to the component of that module.
Considering an example in the previous concept, consider BookComponent. To load it lazily, create
the book-routing.module.ts file inside book folder and map an empty path to BookComponent(Line 8-
10)
1. import { NgModule } from '@angular/core';
The lazy loading and re-configuration will happen only once, i.e., when the route
is first requested. Module and routes will be available immediately for subsequent requests.
Create a book.module.ts file inside book folder and add the following code:
1. import { NgModule } from '@angular/core';
2. import { CommonModule } from '@angular/common';
3. import { BookComponent } from './book.component';
4. import { BookRoutingModule } from './book-routing.module';
5. @NgModule({
6. imports: [CommonModule, BookRoutingModule],
7. declarations: [BookComponent]
8. })
9. export class BookModule { }
In the root routing configuration file app-routing.module, bind 'book' path to the BookModule
using loadChildren property as shown below
Line 12: Binds books path to BookModule using loadChildren property. From v8, Angular
started making use of dynamic imports in lazy loading modules.
Note: Remove BookComponent class from app.module.ts file
Finally, it loads the requested route to the destination book component.
If lazy loading is not added to the demo, it has loaded in 1.14 s. Observe the load time at the bottom of
the browser console. Press F12 in the browser and click Network tab and check the Load time
If lazy loading is added to the demo, it has loaded in 900 ms. As BookComponent will be loaded after
login, the load time is reduced initially
Nested Routes
In Angular, you can also create sub-routes or child routes for your components which means in an
application there will be one root route just like a root component/root module and other routes will be
configured for their respective components. Configuring routes module-wise is the best practice to make
modular Angular applications.
1. Add below code to routing module book-routing.module.ts to implement child routing in the book
module.
2. import { NgModule } from '@angular/core';
3. import { RouterModule, Routes } from '@angular/router';
4. import { BookComponent } from './book.component';
5. import { LoginGuardService } from '../login/login-guard.service';
6. import { DashboardComponent } from '../dashboard/dashboard.component';
7. import { BookDetailComponent } from '../book-detail/book-detail.component';
8. const bookRoutes: Routes = [
9. {
10. path: '',
11. component: BookComponent,
12. children: [
13. { path: 'dashboard', component: DashboardComponent },
14. { path: 'detail/:id', component: BookDetailComponent }
15. ],
16. canActivate: [LoginGuardService]
17. }];
18. @NgModule({
19. imports: [RouterModule.forChild(bookRoutes)],
20. exports: [RouterModule]
21. })
22. export class BookRoutingModule { }
Line 7-16: Child routes can be defined using children property of a route along with path & component
properties. DashboardComponent, BookDetailComponent can be accessed using books/dashboard and
books/detail/: id paths respectively.
Line 18: imports array contains the imported modules to use in the book module. forChild() method adds
routing configurations to the book submodule instead of the root module.
Line 19: exports array contains classes that are exported from the current module.
Line 10: The nested router-outlet is used to render components of this submodule. DashboardComponent
and BookDetailComponent can now be rendered inside Book.component.html. If this nested router-
outlet is not added, child routes will be added to the parent router outlet of the application.
4. Add the below code in app.component.html to add a link for accessing books.
1. <h1>{{title}}</h1>
2. <nav>
3. <a [routerLink]='["/books"]' routerLinkActive="active">Books</a>
4. <a [routerLink]='["/books/dashboard"]' routerLinkActive="active">Dashboard</a>
5. </nav>
6. <router-outlet></router-outlet>
8. Update dashboard.component.html
1. <h3>Top Books</h3>
2. <div class="grid grid-pad">
3. <div *ngFor="let book of books" (click)="gotoDetail(book)" class="col-1-4">
4. <div class="module book">
5. <h4>{{ book.name }}</h4>
6. </div>
7. </div>
8. </div>
9. Update dashboard.component.ts
1. import { Component, OnInit } from '@angular/core';
2. import { Router } from '@angular/router';
3. import { Book } from '../book/book';
4. import { BookService } from '../book/book.service';
5. @Component({
6. selector: 'app-dashboard',
7. templateUrl: './dashboard.component.html',
8. styleUrls: ['./dashboard.component.css']
9. })
10. export class DashboardComponent implements OnInit {
11. books: Book[] = [];
12. constructor(
13. private router: Router,
14. private bookService: BookService) { }
15. ngOnInit(): void {
16. this.bookService.getBooks()
17. .subscribe(books => this.books = books.slice(1, 5));
18. }
19. gotoDetail(book: Book): void {
20. this.router.navigate(['/books/detail', book.id]);
21. }
22. }