Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
100% found this document useful (1 vote)
2K views

Angular Notes

This document provides information on key concepts in Angular including components, directives, data binding, and component interaction. It discusses how to create components and use template syntax to display data and handle user input. Structural and attribute directives like ngIf, ngFor and ngClass are demonstrated. Two-way data binding with ngModel and property, event, style and class binding are covered. Pipes for data transformation are also mentioned.

Uploaded by

Rohan Gaur
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
2K views

Angular Notes

This document provides information on key concepts in Angular including components, directives, data binding, and component interaction. It discusses how to create components and use template syntax to display data and handle user input. Structural and attribute directives like ngIf, ngFor and ngClass are demonstrated. Two-way data binding with ngModel and property, event, style and class binding are covered. Pipes for data transformation are also mentioned.

Uploaded by

Rohan Gaur
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 43

for /f "tokens=5" %a in ('netstat -ano ^| find "4200" ^| find "LISTENING"') do

taskkill /f /pid %a
to clear host cache

to check preinstalled version of angular CLI ->


ng version

to install Angular CLI->


npm install -g @angular/cli@latest

to create new folder ->


ng new structural-directives

what and why angular?


>to build client side application
>great for single page approach
>modular approach and reusable code
>developmemt quicker and easier

parts of angular :
1> angular app- one or more modules
2> modules - one or more components and services
3> components - HTML + class
4> services -business logic

COMPONENT
1> template - View - HTML
2> Class - code - typescript, Data & methods
3> metadata - information - decorator

-
to create new component, write -> ng g c test + enter
selector ka naam -> app-test
app.component.html me likhenge -> <app-test> </app-test> ..... and the new
component will be added.

templateUrl ki jagah likh skte hai template : '<div>inline tempplate </div>',

here for multiline template : `<div>


inline tempate
</div>`

ng serve or npm start inse compile hoga

INTERPOLATION
test.component.ts k andr ->
class k andr ->
public name = "rohan gaur";
aur template : <div>welcome {{name}} </div>
<h2>{{"welcome "+ name}}</h2> //concatenation
<h2>{{name.length}}</h2>
<h2>{{name.toUpperCase()}}</h2>
<h2>{{greetuser()}}</h2>
class k andr ->
greetuser(){return "mr." + this.name}
(NOTE: no assignment statements and accessing global variables )

PROPERTY BINDING

-> Attribute VS Property


they are not same.
attribute - HTTML
properties -DOM(Document Object Model)
attribute initialises DOM properties and then the are done.Attribute value
cannot change once they are initialised.
properties values however can change.

public myId = "testId"; //inside class


<input [id] = "myId" type="text" value="vishwas"> //inside template (property
binding)
or
<input id = "{{myId}}" type="text" value="vishwas">

--> it is same as interpolation... but isme hum boolean values use nhi kr skte
eg->

<input disabled id = "{{myId}}" type="text" value="vishwas"> //read only mode

<input disabled = "false" id = "{{myId}}" type="text" value="vishwas"> //read only


mode

to make it write mode as well we gotta make disabled = "false"

<input [disabled] = "false" id = "{{myId}}" type="text" value="vishwas">// write


mode as well
or
<input bind-disabled = "isdisabled" id = "{{myId}}" type="text" value="vishwas">
with
public isdisabled = true;

CLASS BINDING

write this in template -->

<h2 class = "text-special">


class attribute
</h2>
this is what we have to write in styles, we're making classes here.
styles: [`
.text-success{
color : green;
}
.text-danger{
color : red;
}
.text-special{
font-style : italic;
color : blue;
}
`]

this for class binding -->


<h2 [class] = "successClass">
real class binding
</h2>

and make a property in class as


public successClass = "text-success";

class binding and class attribute together.... it will run class binding wala

<h2 class = "text-success" [class] = "specialClass">


class attribute + class binding
</h2>

another way for conditional class binding i.e. if true then only

<h2 [class.text-danger] = "hasError">


another method of conditional class binding (true/false)
</h2>

ngClass Directive --> for multiple conditional class binding

<h2 [ngClass] = "messageClasses">


another method of multiple conditional class binding (true/false)
</h2>
along with it add this in class below ->
public hasError = false;
public isSpecial = true;
public messageClasses = {
"text-success": !this.hasError,
"text-danger": this.hasError,
"text-special": this.isSpecial
}

STYLE BINDING

add this in template


<h2 [style.color] = "'orange'">
style binding
</h2>\

for conditional style binding


<h2 [style.color] = "hasError ? 'red' : 'green'">
style binding
</h2>

assigning component class property during binding


add this in template
<h2 [style.color] = "highlightcolor">Style Binding 2</h2>
and create this property
public highlightcolor = "pink";

ngStyle dirtective for multiple


<h2 [ngStyle] = "titleStyles">Style Binding 3 </h2>

along with this in class below


public titleStyles = {
color : "blue",
fontStyle : "italic"
}

EVENT BINDING

to capture event we do event binding like mouse click etc.


<button (click) = "onClick()"> greet </button>
{{greeting}}

create the property


public greeting = "";

create this function


onClick(){
console.log('welcome to codevolution');
this.greeting = "vanakkam";
}

or we can
<button (click) = "onClick($event)"> greet </button> // we get access to all the
evnts of DOM event
{{greeting}}
and
onClick(event){
console.log(event);
this.greeting = "vanakkam";
}

another way ->


<button (click) = "greeting = 'welcome rohan '">alt. greet </button>
TEMPLATE REFERENCE VARIABLE

for eg-> input krvaya and button pr click krne pr display krvaya log me .

<input #myInput type = "text">


<button (click) = "logmessage(myInput.value)"> LOG </button>

and add this in class below->

logmessage(val){
console.log(val);
}

TWO WAY BINDING (Banana in the Box)

angular allows us to update a property and at the same time display the changes.

//two way binding


public naam = "";

<input [(ngModel)] = "naam" type = "text"> {{naam}}


+
add somethings to app.module.ts
import { FormsModule } from '@angular/forms'; //add this

imports: [
BrowserModule,
AppRoutingModule,
FormsModule //add this
],

ngIf DIRECTIVE -
STRUCTURAL DIRECTIVE are used to remove HTML elements.
ngIf
ngSwitch
ngFor - used to render the list of html elements

<h2 *ngIf = "true"> Codevolution //ngIf directive .. when true then displayed
else not.
</h2>`,
<ng-template> tag is basically a container for other elements that ngif can used
to add or remove the html DOM.

if else ->

<h2 *ngIf = "displayname; else elseblock">


Codevolution
</h2>
<ng-template #elseblock>
<h2>
Name is hidden
</h2>
</ng-template>

another way for if else

<div *ngIf = "displayname; then thenblock; else elseblock"></div>

<ng-template #thenblock>
<h2> Codevolution </h2>
</ng-template>

<ng-template #elseblock>
<h2> hidden </h2>
</ng-template>

ngSwitch DIRECTIVE -

<div [ngSwitch] = "colour">


<div *ngSwitchCase = "'red'"> you picked red </div>
<div *ngSwitchCase = "'green'"> you picked green </div>
<div *ngSwitchCase = "'blue'"> you picked blue </div>
</div>

public colour = "blue";

ngFor DIRECTIVE
used to render the list of html elements
it is like for loop but the difference is that we render the list of html elements
instead of logic.

with this array -->


public colors = ["red","blue","green","yellow"];

add ->

<div *ngFor = "let i of colors">


<h2> {{i}} </h2>
</div>

modified *ngFor (for index, first, last, odd, even)

<div *ngFor = "let color of colors; index as i; first as f; last as l; odd


as o; even as e">
<h2>{{f}} {{i+1}} {{o}} {{e}} {{color}} {{l}} </h2>
</div>

COMPONENT INTERACTION

sending data from appComponent to testComponent and vice versa. this can be done by
@Input() decorator and @Output() decorator.
app ---> test (input)
test ---> app (output)

SENDING DATA FROM PARENT COMPONENT (app) TO CHILD COMPONENT (test)--->

Step 1 :- create a property {public name = "imrohan";} in {app.component.ts }


Step 2 :- Inside {app.component.html} add the tag with <app-test> attribute
<app-test [parentData] = "name"></app-test>
Step 3:- inside {test.component.ts} create a property like ->
@Input() public parentData;
Step 4:- now, add the Input in import .... like ->
import { Component, OnInit, Input } from '@angular/core';
Step 5:- and now write using interpolation in template
<h2> {{"Hello " + parentData}} </h2>

or for alias name of property in test component we can replace the statement by ->
@Input('parentData') public name;

SENDING DATA FROM CHILD COMPONENT (test) TO PARENT COMPONENT (app)--->


this can be done using events

Step 1:- In {test.component.ts} create the property for event instance


@Output() public childEvent = new EventEmitter();
NOTE - import Output and EventEmitter
Step 2:- set a button to fire the event. which on click will call a method
fireEvent()
<button (click) = "fireEvent()">send event</button>
Step 3:- now create the method
fireEvent(){
this.childEvent.emit('Hey Codevolution');
}
Step 4:- now in {app.component.ts}... create a property -->
public message = "";
Step 5:- now in {app.component.html }.. add this tag in <app-test> attribute.
<app-test (childEvent) = "message= $event"> </app-test>

Thus on clicking the button , Hey codevolution will be displayed .


This message is transfered form test to app

PIPES
allow us to transform data before displaying

<h2> {{naam | lowercase}}</h2> codevolution


<h2> {{naam | uppercase}}</h2> CODEVOLUTION
<h2> {{message | titlecase}}</h2> Welcome To Codevolution
<h2> {{naam | slice:3:7}}</h2> evol
<h2> {{person | json}}</h2> { "firstName": "John", "lastName":
"Suri" }

public naam = "Codevolution";


public message = "Welcome to codevolution";
public person = {
"firstName" : "John",
"lastName" : "Suri"
}

number pipes

<h2> {{5.678 | number : '1.2-3'}}</h2> 5.678


<h2> {{5.678 | number : '3.4-5'}}</h2> 005.6780
<h2> {{5.678 | number : '3.1-2'}}</h2> 005.68

<h2> {{0.25 | percent}}</h2> (percent pipe) 25%


<h2> {{5.678 | currency : 'INR' : 'code'}}</h2> (currency) Rs.
5.678

public date = new Date;

<h2> {{date}} </h2>


<h2> {{date | date : 'short'}} </h2>
<h2> {{date | date : 'shortDate'}} </h2>
<h2> {{date | date : 'shortTime'}} </h2>
<h2> {{date | date : 'medium'}} </h2>
<h2> {{date | date : 'long'}} </h2>

SERVICES
It is a class with a specific purpose.
USES : 1) Share Data (like in below example)
2) Implement application logic (like to calc age if DOB is given)
3) External interactions (connecting to a database)
NAMING CONVENTION : .service.ts

Step 1: create a component through -> ng g c employee-list


Step 2: In {app.component.html} add the attribute ->
<employee-list></employee-list>
Step 3: In {employee-list.component.ts} write folowing ->
An array of employees--->

public employees = [
{"id": 1, "name": "andrew", "age": 30},
{"id": 2, "name": "brandon", "age": 25},
{"id": 3, "name": "christina", "age": 26},
{"id": 4, "name": "elena", "age": 28},
];
and
This in template --->

<h2> Employee List </h2>


<ul *ngFor = "let emp of employees">
<li> {{emp.name}} </li>
</ul>

output --->

Employee List
. andrew
. brandon
. christina
. elena

Now we gotta display all the details in different view

Step 1: Create a new component -> ng g c employee-detail


Step 2: In {app.component.html} add the attribute ->
<employee-detail></employee-detail>
Step 3: add this in template -->
<h2>Details of Employees</h2>
<ul *ngFor = "let emp of employees">
<li> {{emp.id}}. {{emp.name}} - {{emp.age}} </li>
</ul>
*Step 4: we gotta copy that public employee property to this component , but its
not an efficient method... therefore we will be using SERVICES. but before that we
should know about Dependency Injection

DEPENDENCY INJECTION
1)Code without DI - drawbacks
2)DI as design pattern and how it overcomes drawbacks
3)DI as framework in angular.
---------------------------------------------
1) At first engine is not having any parameters in its constructor.
class Engine{
constructor(){}
}
class Tires{
constructor(){}
}

class Car{
engine;
tires;
constructor(){
this.engine = new Engine();
this.tires = new Tires();
}
}

--->but if engine got any parameter, then the car is broken.


class Engine{
constructor(newparameter){}
}
class Tires{
constructor(){}
}

class Car{
engine;
tires;
constructor(){
this.engine = new Engine(); //new Engine(); this will get error
this.tires = new Tires();
}
}
----> i.e. code is not flexible. and every time there will be same engine and same
tires.
----------------------------------------------
2) DI is a coding pattern in which a class receives its dependencies from external
sources rather than creating them itself.
class Car{
engine;
tires;
constructor(engine, tires){
this.engine = engine;
this.tires = tires;
}
}
--> to create the car the code will be like this->

var myEngine = new Engine();


var myTires = new Tires();
var myCar = new Car(myEngine, myTires);
--> now even if engine or tires or both takes parameter, still car will accept it.

var myEngine = new Engine(parameter);


var myTires = new Tires(parameter);
var myCar = new Car(myEngine, myTires);

eg->

var myEngine = new Engine();


var myTires = new Tires();
var depA = new dependency();
var depB = new dependency();
var depAB = new dependency();
var depZ = new dependency(depAB); //*
var myCar = new Car(myEngine, myTires, depA, depB, depZ);
--------------------------------------------
3)We as developers gotta create those parameters first. Rn there are 2
dependencies(engine and tires) so its fine. but if bht sari then difficult.
It has 'injector' which is basically like a container of all the dependencies.

Define the EmployeeService class


Register with Injector
Declare as dependency in employee-list and employee-detail
USING A SERVICE
Step 1: Create a service by -> ng g s employee
Step 2: Create a method to return employee's details in service class -->
getEmployees(){
return [
{"id": 1, "name": "andrew", "age": 30},
{"id": 2, "name": "brandon", "age": 25},
{"id": 3, "name": "christina", "age": 26},
{"id": 4, "name": "elena", "age": 28},
];
}
Step 3: Now go to {app.module.ts}... write in providers metadata ->
providers: [EmployeeService]
and import the EmployeeService class
Step 4: Go to {employee-list.component.ts}... and create a property array -->
public employees = []; //remove the array earlier
Step 5: In constructor, pass this ->
constructor(private _employeeService: EmployeeService) { }
and in ngOnInit() write this-->
ngOnInit() {
this.employees = this._employeeService.getEmployees();
}
and import the necessary class
Step 6: repeat the steps 4 & 5 in {employee-detail.component.ts}

@Injectable({
providedIn: 'root'
})
this is important when the service have dependencies also.
component decorator tells that you might have dependencies.

HTTP AND OBSERVABLES


http mechanism
server
emp-list Get Request |
|-------------> EmpService ============== http ============= | DB
emp-detail Observable Response |

observables->
eg- newspaper company
-> it is a sequence of items that arrive asynchronously over time.
HTTP call - single item
single item - HTTP response

1)HTTP get request from EmpService.


2)Receive the observable and cast it into an employee array
3)Subscribe to the observable from EmpList and EmpDetail
4)Assign the employee array to a local variable

RxJS
-Reactive Extensions for Javascript
-External library to work with observables

FETCH DATA USING HTTP


Step 1: In {app.module.ts},add ->
import { HttpClientModule } from '@angular/common/http'; and
add-> HttpClientModule inside imports.
Step 2: we declare it as a dependency in constructor.
In {employee.service.ts}, inside constructor ->
constructor(private http: HttpClient){} and import the same
http is the local variable which can refer to instance of HttpClient
Step 3: craete a local json file ->
/assets/data/employees.json (location)
and write the following inside json file ->
[
{"id": 1, "name": "andrew", "age": 30},
{"id": 2, "name": "brandon", "age": 25},
{"id": 3, "name": "christina", "age": 26},
{"id": 4, "name": "elena", "age": 28},
{"id": 5, "name": "felicia", "age": 25}
]
Step 4: Now create the get request inside {employee.service.ts} ->
getEmployees(){
return this.http.get(this._url);
}
declare the below property ->
private _url: string = "/assets/data/employees.json";
Step 5: create an employee interface.
in app folder create a new file {employee.ts }-->
export interface IEmployee {
id: number,
name: string,
age: number
}
i.e. each employee has an id , name, and age... we noe have an employee type
where an observable can cast into.
Step 6:Modify in {employee.service.ts} -->
getEmployees(): Observable<IEmployee[]>{ //return type is
observable
return this.http.get<IEmployee[]>(this._url);
}
and import the same.
Step 7: Add these lines in {employee-list.component.ts} and {employee-
detail.component.ts}
ngOnInit() {
this._employeeService.getEmployees()
.subscribe(data => this.employees = data);
}

HTTP ERROR HANDLING


Step 1: In {employee.service.ts} -->
import { Observable,throwError } from 'rxjs';
import { catchError } from 'rxjs/operators'
Step 2: and modify the following things -->
getEmployees(): Observable<IEmployee[]>{
return this.http.get<IEmployee[]>(this._url)
.pipe(catchError(this.errorHandler)) ;
}

errorHandler(error: HttpErrorResponse){
return throwError(error.message || "Server error")
}
Step 3: In {employee-list.component.ts} and {employee-detail.component.ts}-->
public errorMsg; //create this property
and
ngOnInit() {
this._employeeService.getEmployees()
.subscribe(data => this.employees = data,
error => this.errorMsg = error);
}
and display in template --> <h3>{{errorMsg}}</h3>

ROUTING AND NAVIGATION


Routing in Angular->
1)Generate a project with routing option
2)Generate departmentList and employeeList components
3)Configure the routes
4)Add buttons and use directives to navigate

Step 1: Create Routing-demo -> ng new routing-demo --routing


Step 2: Create department-list and employee-list components. ->
ng g c department-list -it -is //inline template and inline style
ng g c employee-list -it -is //inline template and inline style
Step 3: Define all possible routes for our application and each route is an object
in {app- routing.module.ts} ->

const routes: Routes = [


{path : 'departments', component : DepartmentListComponent},
{path : 'employees' , component : EmployeeListComponent}
];
and import the component as well.
//path is reflected in the url and component to be rendered when we navigate
to corresponding path
**note:
we have the same import statement in both {app-routing.module.ts} and
{app.module.ts}
so we better create the array of all rouitng component and thenn export it in app
module
Step 4: In {app-routing.module.ts} ->
export const routingComponent = [DepartmentListComponent,
EmployeeListComponent];
Step 5: In {app.module.ts} , import it ->
import { AppRoutingModule, routingComponent } from './app-routing.module';
and in declaration write --> routingComponent
Step 6: modify the {app.component.html} -->
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Routing and Navigation
</h1>
</div>

<router-outlet></router-outlet>
<!--Routed view goes here-->>
** right now we can access only like --> localhost:4200/departments or
localhost:4200/employees
Step 7: For making buttons type style ->
In {app.component.html} modify after <div> tag-->
<nav>
<a routerLink='/departments' routerLinkActive='active'>Departments</a>
<a routerLink='/employees' routerLinkActive='active'>Employees</a>
</nav>

WILDCARD ROUTE & REDIRECTING ROUTES


To make page not found component.
Step 1: create a new component --> ng g c page-not-found -it-is
Step 2: Add this line in {app.routing.module.ts}-->
{path : '**' , component : PageNotFoundComponent} //another path and
component .. should be at last
now it will be displaying Page Not Found by default .
Step 3: {path : '' , component : DepartmentListComponent},
this will act as default path.
Step 4: {path : '' , redirectTo : '/departments', pathMatch : 'prefix'},
here it will redirect always to 'departments'. therefore it will not work.
//jinka bhi url k aage '' hoga unko rediect krega.. that means sbko hi
krdega.
Step 5: {path : '' , redirectTo : '/departments', pathMatch : 'full'},
we'll be redirected to departments in starting only. but when clicked to
employees, it will be redirected to employees and when wrong address then
redirected to page not found.

ROUTE PARAMETERS
Suppose if i click on departmentList then departmentDetail me display ho ki you
clicked id 1.
Step 1: In {department-list.component.ts} , create an array ->
departments = [
{"id" : 1, "name" : "angular"},
{"id" : 2, "name" : "node"},
{"id" : 3, "name" : "MongoDB"},
{"id" : 4, "name" : "ruby"},
{"id" : 5, "name" : "bootstrap"}
]
Step 2: Edit its template ->(add this)
<ul class = "items">
<li *ngFor = "let department of departments">
<span class = "badge">{{department.id}}</span> {{department.name}}
</li>
</ul>
we'll be having a list. Now we want that if we click on angular then deaprtment/1
should open, and so on...
Step 3: Create a new component -> ng g c department-detail -it -is
Step 4: In {app-routing.module.ts}, create a route ->
{path : 'departments/:id', component : DepartmentDetailComponent}, // :id is
the placeholder
and add the DepartmentDetailComponent in the routingComponent array in {app-
routing.module.ts}... and omit the import from {app.module.ts}
Step 5: In {department-list.component.ts}, set on click event ->(edit this->)
<li (click)="onSelect(department)" *ngFor = "let department of departments">
Step 6: Create an method onSelect(department).In this method we need to navigate to
new route. To navigate in code we make use of router servive , so we'll import and
inject it-->
constructor(private router : Router) { }
Step 7: Write the onSelect() method ->
onSelect(department){
this.router.navigate(['/departments',department.id]);
}
here the navigate function will create the url automatically.
This will simply runs the {department-detail.component.ts}.
Now to display the id on click on screen, we'll edit {department-
detail.component.ts} ->
Step 8: We'll be using activated route service->(edit this ->)
constructor(private route : ActivatedRoute) { }
Step 9: we read the route parameter .we'll get snapshot of current route. and from
this snapshot we use paramMap API, which helps us get the parameter from the url

ngOnInit() {
let id = parseInt(this.route.snapshot.paramMap.get('id'));
this.departmentId = id;
}
Step 10: Now display the id by creating public departmentId; and this.departmentId
= id; and then->
<h3>
you select the department by id: {{departmentId}}
</h3>

**We use Router service and ActivatedRoute service to navigate and get the id from
it respectively.
paramMap OBSERVABLE
Consider if we want to put two buttons previous and next so as to navigate. So what
we can do is->
Step 1: In {department-detail.component.ts}, make two anchors with event listner in
template->
<a (click) = "goPrevious()">Previous</a>
<a (click) = "goNext()">Next</a>
and import router also.. and create router service as well
Step 2: Create the methods , and navigate ki madad se navigate kro -->
goPrevious(){
let previousId = this.departmentId - 1;
this.router.navigate(['/departments',previousId]);
}
goNext(){
let nextId = this.departmentId + 1;
this.router.navigate(['/departments',nextId]);
}
But the problem here is that the url will change and work fine but the value of id
in view will not change. This is the drawback of snapshot appraoch.When we're
navigating from one component back to the same component, the snapshot approach
will not work. Bcoz it will reuse the same component. Thus the ngOnInit method will
not be called again and the id will not be renewed from url.

Step 3: remove the snapshot thingy.. add -->


this.route.paramMap.subscribe((params: ParamMap) => { //arrow function
let id = parseInt(params.get('id'));
this.departmentId = id;
});
and import ParamMap form routers.
OPTIONAL ROUTE PARAMETERS
OUTPUT:-
1 angular
2 node
3 MongoDB
4 ruby
5 bootstrap
Suppose maine (2 node) ko select kiya then vha se back button pr click krne pr mai
chahata hu ki node alag colour me aa jaye toh this can be done with the help of
optional routing.
Step 1: Create the back button in {department-details.component.ts} -->(in
template)
<div>
<button (click) = "gotoDepartments()">BACK</button>
</div>
Step 2: Create the gotoDepartments() method -->
gotoDepartments(){
let selectedId = this.departmentId ? this.departmentId :null; //save the
id in selectedId
this.router.navigate(['/departments', {id : selectedId}]); //pass this as
optional para.
}
In output, when we click on back button, we get the desired department id in the
url.
http://localhost:4200/departments;id=2
this id=2 at last is optional as it does not affect our view but this id can be
used for other things

Step 3: In {department-list.component.ts} add the activated router service in


constructor-->
constructor(private router : Router , private route : ActivatedRoute) { }
Step 4: Now retrieve the id from the url, modify the ngOnInit() method-->
ngOnInit() {
this.route.paramMap.subscribe((params: ParamMap) => {
let id = parseInt(params.get('id'));
this.selectedId = id;
});
}
Now we have the Id (selectedId).
Step 5: Create a method which works when id is equal to the selected id.
isSelected(department){
return department.id === this.selectedId;
}
Now we will use this method through class binding. -->
Step 6: Modify the template part [.selected class is in {style.css} already] ->
<ul class = "items">
<li (click)="onSelect(department)"
[class.selected]="isSelected(department)" *ngFor =
"let department of departments">
<span class = "badge">{{department.id}}</span> {{department.name}}
</li>
</ul>
**Note - optional route parameter does not need an placeholder while configuring
the route.
RELATIVE NAVIGATION
Till now we have used absolute routes. This starts with a slash (/) . But this
reduce the flexibility of routes.
For eg-> if we gotta change the name of the link from departments to department-
list , then the one way is to replace all departments by department-list in
{department-list.component.ts}, and {department-detail.component.ts} and
{app.component.html}.
Another way is RELATIVE NAVIGATION.
Step 1: In {app-routing.module.ts}, replace all departments from department-list.
const routes: Routes = [
{path : '' , redirectTo : '/department-list', pathMatch : 'full'},
{path : 'department-list', component : DepartmentListComponent},
{path : 'department-list/:id', component : DepartmentDetailComponent},
{path : 'employees' , component : EmployeeListComponent},
{path : '**' , component : PageNotFoundComponent}
];
Step 2: Now in {department-list.component.ts}, modify onSelect() method -->
onSelect(department){
//this.router.navigate(['/department-list',department.id]);
this.router.navigate([department.id] , {relativeTo : this.route});
}
Step 3: In {department-detail.component.ts}, modify the 3 methods -->
goPrevious(){
let previousId = this.departmentId - 1;
//this.router.navigate(['/department-list',previousId]);
this.router.navigate(["../", previousId ], { relativeTo: this.route});
}
goNext(){
let nextId = this.departmentId + 1;
//this.router.navigate(['/department-list',nextId]);
this.router.navigate(["../", nextId ], { relativeTo: this.route});
}
gotoDepartments(){
let selectedId = this.departmentId ? this.departmentId :null;
//this.router.navigate(['/department-list', {id : selectedId, test :
'testvalue'}]); // it wont gonna matter if we omit testvalue as its optional
this.router.navigate(['../', {id : selectedId}] , {relativeTo :
this.route});
}
output -> http://localhost:4200/department-list/3
CHILD ROUTES
Suppose detail component k andr hume 2 button bnane hai jinhe dabane se overview
and contact dikhe.
Step 1: Create two components --> ng g c department-overview -it -is
and
ng g c department-contact -it -is
Step 2: We want to add them in department-detail component, so in {app-
routing.module.ts} -->
{
path : 'department-list/:id',
component : DepartmentDetailComponent,
children : [
{path : 'overview', component : DepartmentOverviewComponent},
{path : 'contact', component : DepartmentContactComponent}
]
},
and import the same.. and add them routingComponent Array.. and remove the import
statements from {app.module.ts}.
Step 3: Now place the router Holder and craete the buttons in {department-
detail.component.ts}-->

<p>
<button (click) = "showOverview()" > Overview </button>
<button (click) = "showContact()"> Contact </button>
</p>

<router-outlet></router-outlet>
Step 4: Now create these methods -->
showOverview(){
this.router.navigate(['overview'], {relativeTo : this.route});
}

showContact(){
this.router.navigate(['contact'], {relativeTo : this.route});
}

___________________________________________________________________________________
__________________

ANGULAR FORMS
We gotta track the following for an efficient form ->
1) Data binding
2) Change tracking
3) validation
4) visual feedback
5) Error messages
6) Form submission

Template ----------> class ---------> service ----------> server


collect data bind data send data

Two approaches ->


Template driven forms
Reactive forms

TEMPLATE DRIVEN FORMS (TDF)

1) two way binding with ngModel


2) BUlky HTML and minimal component code
3) Automatically tracks the form and form elements state and validity
4) Unit testing is a challenge
5) Readibility decreases with complex forms and validation
6) suitable for simple scenarios

Steps of working with TDF->


1) Generate new CLI project
2) Add the form HTML
3) Binding data
4) Tracking state and validity
5) Providing visual feedback
6) Displaying error messages
7) Posting data to a server

SETTING UP A NEW PROJECT

1) Create a new folder -> Angular Forms


2) Create the new project -> ng new tdf
3) run the command inside tdf project-> ng serve -o
4) Now add the bootstrap styling to our project.-> Go to getbootstrap.com... and
copy the css wali line.
5) And paste it to {index.html} at the end of the <head> tag.
6) To check its working, open app.component.html, and remove everything... now add
a button with the following classes ->
<button class = "btn-primary btn">Submit</button> // btn and btn-primary are two
classes
----------->OUTPUT-> It will display a blue colour submit button.

ADDING FORM HTML


We'll be creating a bootcamp enrollment form.
In {app.component.html}-->

<div class = "container-fluid">


<h1>Bootcamp Enrollment Form</h1>
<form>
<div class = "form-group">
<label>Name</label>
<input type = "text" class = "form-control">
</div>

<div class = "form-group">


<label>Email</label>
<input type = "email" class = "form-control">
</div>

<div class = "form-group">


<label>Phone</label>
<input type = "tel" class = "form-control">
</div>

<div class = "form-group">


<select class = "custom-select">
<option selected>I am intrested in</option>
<option *ngFor = "let topic of topics"> {{topic}} </option>
</select>
</div>

<div class = "mb-3">


<label>Time preference</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="timePreference" value
="morning">
<label class="form-check-label">Morning (9am -12pm)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="timePreference" value
="evening">
<label class="form-check-label">Evening (5pm -8pm)</label>
</div>
</div>

<div class="form-check mb-3">


<input class="form-check-input" type="checkbox">
<label class="form-check-label">
Send me promotional offers
</label>
</div>

<button class="btn btn-primary" type="submit">Submit form</button>

</form>
</div>

BINDING DATA WITH ngForm


step 1: In {app.module.ts} -> import { FormsModule } from '@angular/forms';
and in import write FormsModule
Anytime we use a <form> tag, angular attaches a ngForm derective to form tag which
gives valuble information of that particular form. It tells the values of different
form controls and weather the values are valid or invalid. This will be done with
the help of template reference variable.

<form #userForm= "ngForm">

<pre>{{userForm.value | json}}</pre>

khali isko run krenge toh kuch nhi ayega ... output -> {}

hume btana pdega ki kiski value capture kre.... so write 'ngModel' after 'form-
control' jiski bhi value capture krvani hai.

ab run krenge toh srf likha ayega {timePreference : "morning"}... coz humne name
srf usi me dala hai..
so now ngModel k saath saath name bhi daal do ..
eg-> <input type = "tel" class = "form-control" name="phone" ngModel>

now run. we get all the values of the form.

Suppose we gotta add the address ... it has different subparts.. street,
city,state,pincode,etc.
So this can be done by ngModelGroup directive.

<div ngModelGroup="address"> //ngmodelgroup


<div class = "form-group">
<label>Street</label>
<input type = "text" class = "form-control" name="street" ngModel>
</div>
<div class = "form-group">
<label>City</label>
<input type = "text" class = "form-control" name="city" ngModel>
</div>
<div class = "form-group">
<label>State</label>
<input type = "text" class = "form-control" name="state" ngModel>
</div>
<div class = "form-group">
<label>Postal code</label>
<input type = "text" class = "form-control" name="postalCode" ngModel>
</div>
</div>

final output ->

{
"address": {
"street": "j-79 Jai prakash nagar, west ghonda",
"city": "delhi",
"state": "Delhi",
"postalCode": "110053"
},
"userName": "Rohan Gaur",
"email": "imrohangaur@gmail.com",
"phone": "+919911012436",
"topic": "",
"timePreference": "",
"subscribe": ""
}

BINDING DATA TO A MODEL


Imagine of a edit button, on clickig of which you got your details prefilled, which
you can edit.... this can be done by binding data to a model, which can be send to
server after validation.

Step 1: create a class named user --> ng generate class User


Step 2: In {user.ts}, create a constructor. -->

export class User {


constructor(
public street: string,
public name: string,
public email: string,
public phone: number,
public topic: string,
public timePreference: string,
public subscribe: boolean
) {}
}

Step 3: In {app.component.ts}, create a property. -->


userModel = new User('j-79,jp nagar','rob', 'rob@test.com',565656565, '',
'morning', true);
and import the user as well.
Step 4: Now in {app.component.html}, we will do property binding. But first display
userModel using interpolation.
<hr />
<pre>{{userModel | json}}</pre>
Step 5: Now replace all ngModel by [ngModel]=userModel.name... and like this.
But property binding will only be a one way binding. In forms we'll be needing two
way binding. Thus we will use banana in the box.
Step 6: Replace all [ngModel]=userModel.name by [(ngModel)]=userModel.name .

TRACK CONTROL STATE AND VALIDITY

Following classes are applied by angular which can be used for validation.

STATE CLASS IF TRUE CLASS IF FALSE


The control has been visited. ng-touched ng-untouched
The control's value has changed. ng-dirty ng-pristine
The control's value is valid. ng-valid ng-invalid

Step 1: to check the classes applied-->


Create a reference variable to name let say. and then display it through
interpolation. <input type = "text" required #name class = "form-
control" name="userName"
[(ngModel)]="userModel.name">

{{name.className}} //display

ngModel properties -> required, valid, pristine, touched, untouched, dirty,


invalid.
Step 2: To use the property -->
assign the reference variable to ngModel. --> #email="ngModel"
Now display the properties --> {{email.untouched +" "+ email.valid + " "
+ email.pristine}}
OUTPUT -> true true true... true false true
VALIDATION WITH VISUAL FEEDBACK

for visual feedback if we apply 'is-invalid' directly to the class... then we'll
always be getting red box. -->
<....#name="ngModel" class="form-control is-invalid"....>

This is not what we want.. Therefore we will be applying this along with some
condition. -->
<....#name="ngModel" [class.is-invalid]="name.invalid" class="form-
control"....>
This will be read as, "Apply the class 'is-invalid', when the 'name' form-control
is invalid ".
Now suppose ive just loaded the form.. then my phone number wala input would be
empty.. that will gonna show red block.. so to solve the problem -->

<div class = "form-group">


<label>Phone</label>
<input type = "tel" required #phone="ngModel" pattern="^\d{10}$" [class.is-
invalid]="phone.invalid && phone.touched"
class = "form-control" name="phone" [(ngModel)]="userModel.phone">
</div>

This pattern thing ensure the number should be of 10 digits only.We've done class
binding (bootstrap)

DISPLAYING ERROR MESSAGES

Step 1: After <input> tag.. write <small> tag for error message.
<small class="text-danger" [class.d-none]="phone.valid ||
phone.untouched">Phone number must
be 10 digits</small> "Do not show error if phone is valid or is untouched".
class is "text-danger".. this is from bootstrap. This will show the message in
red colour.

For showing multiple errors for multiple conditions->


Step 2:
<!-- <small class="text-danger" [class.d-none]="phone.valid ||
phone.untouched">Phone number must be 10 digits</small> -->
//comment the earlier one
<div *ngIf = "phone.errors && (phone.invalid || phone.touched)">
<small class="text-danger" *ngIf="phone.errors.required">Phone Number is
required</small>
<small class="text-danger" *ngIf="phone.errors.pattern">Phone number must be
10 digits</small>
</div>
**pattern for email verification ->
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"

SELECT CONTROL VALIDATION


Validation for select tag.

<div class = "form-group">


<select required #topic="ngModel" [class.is-invalid]="topic.invalid &&
topic.touched" class = "custom-select" name="topic" [(ngModel)]="userModel.topic">
<option value="">I am intrested in</option>
<option *ngFor = "let topic of topics"> {{topic}} </option>
</select>
<small class="text-danger" [class.d-none]="topic.valid ||
topic.untouched">Please choose a topic</small>
</div>

Put required.. create a ngModel refernce.. Use ngModel properties for validation.
Use class binding .. After <select> tag, add <small> tag.. with class=text-danger.
and do not show the error when the phone is valid or untouched.

The above thing will work only when the string passed and value="" is empty. ,, if
we pass a default value, then it will fail. -->
userModel = new User('mgklsjgojsdogjsi','', 'rob@test.com',8989898989, 'default',
'morning', true);
and set value="default";

Step 1: For solving the problem we will be using the (blur) and (change)
listener...
<select (blur)="validateTopic(topic.value)"
(change)="validateTopic(topic.value)".....>

Step 2: In {app.component.ts}, create a property ->


topicHasError= true;
Step 3: create the method for validation ->
validateTopic(value){
if(value === "default"){
this.topicHasError = true;
}
else
this.topicHasError = false;
}
Step 4: In {app.component.html} replace 'topic.invalid' by 'topicHasError'.. and
'topic.valid' by
'!topicHasError'
[class.is-invalid]="topicHasError && topic.touched"
<small class="text-danger" [class.d-none]="!topicHasError ||
topic.untouched">Please choose a topic</small>

FORM VALIDATION
We can add form validation in the <form> tag.
Step 1: Create the reference varibale to ngForm -->
<form #userForm= "ngForm">

{{userForm.form.valid}} //OUTPUT = true (if valid)


Step 2: Now we want to disable the submit button if form is invalid.-->
<button [disabled]="userForm.form.invalid " class="btn btn-primary"
type="submit">Submit
form</button>
We have bind the class [disabled] when the (userForm.form.invalid)i.e. when the
form is invalid .
But the problem is that it does not apply to select field.
that means if we do not select the feild, the form will submit then too.
To solve the problem ... follow step 3.
Step 3: add this condition as well to [disabled] . -->
[disabled]= "userForm.form.invalid || topicHasError"

SUBMITTING FORM DATA

Step 1: In {app.component.html}, update the <form> tag. Add novalidate attribute to


prevent browser validation from kicking in when we click on submit button.
Step 2: Bind to the ngSubmit event which gets emitted when the submit button is
clicked.

<form #userForm= "ngForm" (ngSubmit)="onSubmit()" novalidate >

Step 3: In {app.component.ts}, create the onSubmit() method -->


onSubmit(){
console.log(this.userModel);
}
Now run this , you will get the form inputs in the console.
To Submit this data to a server -->
Step 4: Create a service file-> ng g s enrollment
Step 5: In {enrollment.service.ts}, inject the HttpClient and import the same -->

import { HttpClient } from '@angular/common/http';


constructor(private _http: HttpClient) { }
Step 6: Create a variable _url to which we post the data.
_url = "";
Step 7: Also include the HttpClientModule in {app.module.ts}.
import { HttpClientModule } from '@angular/common/http';
and in imports write --> HttpClientModule
Step 8: Now we will create the mehtod enroll(), which will make the post request.

enroll(user: User){
return this._http.post<any>(this._url, user)
}
It will accept an argument user of type User.(import the same). This post requst
will return as an observable . which we gotta subscribe to observable. In
{app.component.ts}
Step 9: Import the EnrollmentService and inject it.
import { EnrollmentService } from './enrollment.service';
constructor(private _enrollmentService: EnrollmentService){}
Step 10: call the enroll method inside the onSubmit method and subscribe to the
observable.
onSubmit(){
this._enrollmentService.enroll(this.userModel)
.subscribe(
data => console.log('Success!', data),
error => console.error('Error', error)
)
}
EXPRESS SERVER TO RECEIVE FORM DATA
w'll setup basic express server which will receive form data.
Step 1: Create a new folder called as "server". And now initialise a json file. for
this right click and then open in terminal.
Step 2: write -------> npm init --yes //We'll have package.json file
Step 3: Lets install the dependencies-->
npm install --save express body-parser cors
express is a web server. body-parser is a middleware to handle form data such as
user registeration and login. cors is a package to make request across different
ports.
Step 4: Create a new file -> server.js and write down the following stuff. ==>

const express = require('express');


const bodyParser = require('body-parser');
const cors = require('cors');

const PORT = 3000; //port no. that our express server will
run on

const app = express (); //create the intance of express

app.use(bodyParser.json()); //bodyParser to handle json data

app.use(cors());

app.get('/', function(req,res) { //call back function as 2nd argument


res.send('hello from server'); // respond .send. and the msg.
})

app.post('/enroll', function(req,res){
console.log(req.body);
res.status(200).send({"message": "Data received"});
})

app.listen(PORT,function(){ //node
server
console.log("server running on localhost:" + PORT);
});

Step 5: Fill the url --> _url = "http://localhost:3000/enroll";

Now what we want that after clicking on the submit button , we want to prevent the
resubmition of the form.
Step 6: In {app.component.ts} -->
submitted = false; //create a flag to keep eye on submission
and include this inside the onSubmit() -->
this.submitted = true;

Step 7: Now i am just disappearing the whole form on submission -->


<form #userForm= "ngForm" *ngIf="!submitted" (ngSubmit)="onSubmit()"
novalidate >
ERROR HANDLING
Step 1: In {enrollment.service.ts}, import catchError and throwError.
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
Step 2: Use pipe to catch error and throw error
enroll(user: User){
return this._http.post<any>(this._url, user)
.pipe(catchError(this.errorHandler))
}

errorHandler(error : HttpErrorResponse){ //method handle the


error
return throwError(error);
}

Right now we're just displaying the error in the console,. instead bind the error
status text to the view.
Step 3: In {app.component.ts}, --> errorMsg = "";
And in onSubmit() method modify the error statement -->
error => this.errorMsg = error.statusText
Finally bind this message in the html
Step 4: Write the <div> tag before <form> tag -->
<div class= "alert alert-danger" *ngIf="errorMsg">
{{errorMsg}}
</div>
Step 5: In {server.js}, change the 200 to 401 -->
res.status(401).send({"message": "Data received"});

now run the server by --> node server

OUTPUT: after submitting we get the error of unauthorised.

___________________________________________________________________________________
__________________
TDF AND REACTIVE (MODEL DRIVEN) APPROACH
Step 1: In {app.component.html}, in form tag, in onSubmit() method, pass the
userForm as the parameter.
<form #userForm= "ngForm" *ngIf="!submitted"
(ngSubmit)="onSubmit(userForm)" novalidate >
Step 2: In {app.component.ts}, In onSubmit method, receive the method, and omit the
rest of the things ->
onSubmit(userForm){
console.log(userForm);
/*this.submitted = true;
this._enrollmentService.enroll(this.userModel)
.subscribe(
data => console.log('Success!', data),
//error => console.error('Error', error)
error => this.errorMsg = error.statusText
)*/
}

OUTPUT: ngForm (in console).

REACTIVE FORMS
1) Code and logic resides in the component class.
2) No two way binding.
3) well suited fro complex scenarios.
4) Dynamic form fields... for eg -> proving an option to add additional fields to
add alternate phone no.
5) Custom validation. eg-> password and confirm password validation.
6) Dynamic validation. for eg-> if wanna subscribe to updates, then making email
field as mandatory.

Procedure -->
1) CLI generated project
2) Add the form HTML
3) Create the form model
4) Manage the from control values
5) FormBuilder service
6) Validation- sinple, custom, cross-field and dynamic
7) Dynamic form controls
8) submitting form data
ADDING FORM HTML
Step 1: Create a new project --> ng new reactive-form
Step 2: Add the bootstarp link in {index.html}.. after the other link.
Step 3: now open the {app.component.html}, and remove all the things already
present there. and add->
registration form there with 2 input fields. and a button.class= "form-group"
in div and "form-control" in input. and btn btn-primary in button.

CREATING THE FORM MODEL


Step 1: In {app.module.ts},import the ReactiveFormsModule from @angular/forms and
write it in import.
Two classes are the building blocks of reactive forms. , form-group and form-
control.
Step 2: We gotta create the form model -->
In {app.component.ts}, create the registrationForm of type FormGroup, and
pass an object inside it with its controls...
registrationForm = new FormGroup({
userName: new FormControl('Rohan'), //default value
password: new FormControl(''),
confirmPassword: new FormControl('')
});
Step 3: To associate this model with the view which is our html form. For that
ReactiveFormsModule provide us with certain directives.
We use the formGroup directive and bind the registrationForm group.
<form [formGroup]="registrationForm">
Step 4: to bind each of the form control , we use formControlName directive
<input formControlName="userName" type="text" class="form-control>
Step 5: To visulaise the communication lets use the interpolation on
registrationForm->
{{registrationForm.value | json}} //after <form> tag.
NESTING FORM GROUPS
Step 1: Create 3 forms controls, city state and postalcode. under the form group ,
address.
In {app.component.ts},
registrationForm = new FormGroup({
userName: new FormControl('Rohan'),
password: new FormControl(''),
confirmPassword: new FormControl(''),
address: new FormGroup({
city: new FormControl(''),
state: new FormControl(''),
postalCode: new FormControl('')
})
});
Step 2: Now create the 3 inputs inside {app.component.html}-->
<div formGroupName="address"> //formGroupName instead of
[formGroup]="address"
<div class="form-group">
<label>City</label>
<input formControlName="city" type="text" class="form-control">
</div>

<div class="form-group">
<label>State</label>
<input formControlName="state" type="text" class="form-control">
</div>

<div class="form-group">
<label>Postal code</label>
<input formControlName="postalCode" type="number" class="form-
control">
</div>
</div>

OUTPUT: {
"userName": "Rohan",
"password": "fd",
"confirmPassword": "ff",
"address": {
"city": "fs",
"state": "sf",
"postalCode": 545
}
}
MANAGING CONTROL VALUES
Suppose we want to create a button on clicking on which the form load the values
from API and fill in the form.
Step 1: Create a button for loading the API
<button (click)="loadApiData()" class="btn btn-secondary ml-3"
type="button">Load API
Data</button>
Step 2: Now we will create this loadApiData() method in {app.component.ts}-->
loadApiData(){
this.registrationForm.setValue({ //we gotta set value of
registration form
userName: 'Bruce',
password: 'test',
confirmPassword: 'test',
address: {
city: 'city',
state: 'state',
postalCode: 123456
}
})
}
On clicking the Load API DAta button the fields will get filled by dummy data.
If we dont want the address field to get filled when the button is pressed ., and
for that if we simply remove the address wala section then the thing will not
work,, the userName, passwrd thing will also not get filled.
For solving this problem , either simply put the string empty, like ->
address: {
city: '',
state: '',
postalCode: 123456
}
or
We can use the patchValue instead of setValue and remove the address thing.
FORMBUILDER SERVICE
We can generate the form model by FormGroup and FormControl classes. but this
becomes very repetitive. So to avoid this angular provides us with FormBuilders
service which inturn provides method to handle generating form controls.

Step 1: In {app.component.ts}, import the FormBuilder and remove the FormGroup and
FormControl.
import { FormBuilder } from '@angular/forms';
Step 2: Now inject it in constructor.
constructor(private fb: FormBuilder){}
Step 3: To use this instance to generate the form control. Comment the previous
form model .
registrationForm = this.fb.group({
userName: ['Rohan'], //first element in the array is the
default value
password: [''],
confirmPassword: [''],
address: this.fb.group({
city: [''],
state: [''],
postalCode: ['']
})
});
Its just an aternative to create formGroup and formControl.

SIMPLE VAIDATION
Firstly we'll apply the simple validation.
Step 1: In {app.component.ts}, import the Validators.
import { FormBuilder , Validators } from '@angular/forms';
and
Add the validation in userName field
userName: ['Rohan',Validators.required], //second element is for
validation
Step 2: To provide visual feedback to the user that something is not right.
In {app.component.html}, add class binding in username.
[class.is-invalid]="registrationForm.get('userName').invalid &&
registrationForm.get ('userName').touched"
Step 3: to display the error msg ->
<small class= "text-danger" [class.d-
none]="registrationForm.get('userName').valid ||

registrationForm.get('userName').untouched">
Username is required</small>
Step 4: If there are multiple validation statements...
in {app.component.ts}, add the multiple validation statements.
userName: ['Rohan',[Validators.required, Validators.minLength(3)]],
Step 5: Now different error msgs for different errors. -->
<!-- <small class= "text-danger" [class.d-
none]="registrationForm.get('userName').valid ||

registrationForm.get('userName').untouched">
Username is required</small> -->

<div *ngIf="registrationForm.get('userName').invalid && registrationForm.get

('userName').touched">
<small *ngIf="registrationForm.get('userName').errors?.required"
class="text-danger">Username
is required</small>
<small *ngIf="registrationForm.get('userName').errors?.minlength"
class="text-danger">(min 3
character)</small>
</div>

'?' is safe navigation operator


**NOTE :
As we can see that this {registrationForm.get('userName')} thingy has been used a
lot.
This amkes the code looks much typical.
So for the sake of simplicity , we can use the getter.
In {app.component.ts}, write the getter
Step 1: get userName(){
return this.registrationForm.get('userName');
}
Step 2: Replace all the {registrationForm.get('userName')} with just {userName} in
{app.component.ts}

**NOTE -> Select All Occurrences of Find Match {ctrl + shift + L} and {alt +
F3}

CUSTOM VALIDATION (hard)


Suppose hume kuch aesa validation lgani hai jo build in available in hai... like
admin as a name use krna.... So we'll be using custom validation.
A custom validator is a function. It could be written into the component file
itself. But since validator functions are generally reused in several places in
your application so ots always agood idea to crate a seperate file and export them.

Step 1: create a file {user-name.validator.ts}, inside /app/shared/


Step 2: write this thing -->
export function forbiddenNameValidator(control: AbstractControl): {[key:
string]: any} | null{
const forbidden = /admin/.test(control.value);
return forbidden ? {'forbiddenName':{value: control.value}} : null ;
}

Step 3: In {app.component.html}, add this small tag for error displaying -->
<small *ngIf="userName.errors?.forbiddenName" class="text-danger">
{{userName.errors?.forbiddenName.value}} Not Allowed</small>

Now this code wont let you type admin in the username field in any manner (except
case sensitive).
To forbid more than one strings. -->
Step 4: comment the earlier one .. and add the code below instead -->
export function forbiddenNameValidator(forbiddenName: RegExp) : ValidatorFn{
return (control: AbstractControl): {[key: string]: any} | null => {
const forbidden = forbiddenName.test(control.value);
return forbidden ? {'forbiddenName':{value: control.value}} : null ;
};
}

Step 5: in {app.component.ts} -->


userName: ['Rohan',[Validators.required,
Validators.minLength(3),forbiddenNameValidator(/password/),forbiddenNameValidator(/
admin/)]],

CROSS FIELD VALIDATION


Like confirm password .
Step 1: Create the file inside the shared folder --> {password.validator.ts}
Step 2: Write a function that will take control and return the object of key in
string and null
export function PasswordValidator(control : AbstractControl): { [key: string]
: boolean} | null {
const password = control.get('password');
const confirmPassword = control.get('confirmPassword');

if(password.pristine || confirmPassword.pristine){ //This thing will not show


an error untill
return null; //the confirmpswd thing is empty
and untouched
}

return password && confirmPassword && password.value !==


confirmPassword.value ? {'misMatch': true} : null;
}

Step 3: This validator is applied on the formGroup only ... i.e. on the
registrationForm .. not on the userName or password or confirmPassword.

In {app.component.ts}, add the validator to the registration form .


state: [''],
postalCode: ['']
})
}, {validators : PasswordValidator}); // as an object
Step 4: in {app.component.html}, edit the confirmPassword field as follows -->
<div class="form-group">
<label>Confirm password</label>
<input [class.is-invalid]="registrationForm.errors?.misMatch"
formControlName="confirmPassword"
type="password" class="form-control">
<small class="text-danger" *ngIf="registrationForm.errors?.misMatch
">Password do not match</small>
</div>

CONDITIONAL VALIDATION

Step 1: Lets make an email input fiels and also a checkbox to subscribe promotional
offers.
<div class="form-group">
<label>Email</label>
<input formControlName="email" type="email" class="form-control">
</div>

<div class="form-check mb-3">


<input formControlName="subscribe" type="checkbox" class="form-check-input">
<label class="form-check-label">Send me promotional offers</label>
</div>
Step 2: Few changes in {app.component.ts} -->
email: [''],
subscribe: [false],
and add the fields in load Api data also.
Step 3: implements the OnInit in the class. -->
export class AppComponent implements OnInit{
Step 4: Now create the ngOnInit() method and cut-paste the formBuilder wala code
inside it.
ngOnInit(){
this.registrationForm = this.fb.group({
userName: ['Rohan',[Validators.required, Validators.minLength
(3),forbiddenNameValidator(/password/),forbiddenNameValidator(/admin/)]],
email: [''],
subscribe: [false],
password: [''],
confirmPassword: [''],
address: this.fb.group({
city: [''],
state: [''],
postalCode: ['']
})
}, {validators : PasswordValidator});
}

For error removal create the --> registrationForm : FormGroup.


and Now change registrationForm to this.registrationForm.

Step 5: Now add the valueChange to the checkBox and subscribe to it. If
checkedValue is true, then set the setValidators, else not -->(inside the
ngOnInit() method)

this.registrationForm.get('subscribe').valueChanges
.subscribe(checkedValue =>{
const email = this.registrationForm.get('email');
if(checkedValue){
email.setValidators(Validators.required);
}else{
email.clearValidators(); //clear the validators
}
email.updateValueAndValidity(); //to make sure that the correct
status is reflected
});

Step 6: Create a getter for email control just like the userName control. -->
get email(){
return this.registrationForm.get('email');
}
Step 7: In {app.component.html}, make the changes for error display.(only email tag
will be altered).

<div class="form-group">
<label>Email</label>
<input [class.is-invalid]="email.invalid && email.touched"
formControlName="email" type="email" class="form-control"> //this 'email' is that
getter only.
<small class= "text-danger" [class.d-none]="email.valid || email.untouched">
email is required</small>
</div>
DYNAMIC FORM CONTROL
To add more form fields and fill them all. i.e. form will be extended only when
necessary.
Button to duplicate the email address field -->
Step 1: In {app.component.ts}, firstly import FormArray Class which maintain the
dynamic list of control.
Step 2: Add alternateEmails in the form model as an array.

address: this.fb.group({
city: [''],
state: [''],
postalCode: ['']
}),
alternateEmails: this.fb.array([]) // this line is added
}, {validators : PasswordValidator});
Step 3: Create a getter -->
get alternateEmails(){
return this.registrationForm.get('alternateEmails') as FormArray;
}
Step 4: Create the method that will be called to dynamically insert form controls
into the form array
addAlternateEmail(){
return this.alternateEmails.push(this.fb.control(''));
}
Every time this method is called, a form control is pushed into a form array.
Step 5: Now create the button in {app.component.html} inside the email wala <div>
after <label>-->
<button type="button" class="btn btn-secondary btn-sm m-2"
(click)="addAlternateEmail
()">Add email</button>
Step 6: Now we gotta iterate over the form array and display it.-->
<div formArrayName ="alternateEmails" *ngFor="let email of
alternateEmails.controls; let
i=index">
<input type="text" class="form-control my-1" [formControlName]="i">
</div>
//we need to bind to formControlName, the index. Keeping in mind the array is
dynamic.
SUBMITTING FORM DATA
Step 1: Add (ngSubmit) inside <form> tag
<form [formGroup]="registrationForm" (ngSubmit) ="onSubmit()" >
Step 2: In {qpp.component.ts}, create the method onSubmit() -->
onSubmit(){
console.log(this.registrationForm.value);
}
Step 3: To send this data to a server, we gotta use service -->
create the service --> ng g s registration
Step 4: In {registration.service.ts}, import the HttpClient and inject it.
import { HttpClient } from '@angular/common/http';
and
constructor(private _http: HttpClient) { }
Step 5: And now import the HttpClientModule in {app.module.ts}.
import { HttpClientModule } from '@angular/common/http';
and
add HttpClientModule in imports.
Step 6: In {registration.service.ts}, create the property -->
_url = 'http//localhost:300/enroll';
Step 7: Create the method for posting the userData to the server.
register (userData){
return this._http.post<any>(this._url,userData);
}
Step 8: In {app.component.ts}, subscribe to the observable return by register
function -->
onSubmit(){
console.log(this.registrationForm.value);
this._registrationService.register(this.registrationForm.value)
.subscribe(
response => console.log('Success!', response),
error => console.log('Error!', error)
)
}
and
constructor(private fb: FormBuilder, private _registrationService:
RegistrationService){}
Step 9: Now lets disable the submit button if invalid.
<button [disabled]="!registrationForm.valid" class="btn btn-primary"
type="submit">Register</button>
Step 10: In CLI run the command inside the path /angular-form/server/ -->
node server
OUTPUT : {userName: "Rohan", email: "imrohangaur@gmail.com", subscribe: true,
password: "ghgh", confirmPassword: "ghgh",��}
Success! {message: "Data received"}

You might also like