Angular Notes
Angular Notes
taskkill /f /pid %a
to clear host cache
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.
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
--> it is same as interpolation... but isme hum boolean values use nhi kr skte
eg->
CLASS BINDING
class binding and class attribute together.... it will run class binding wala
another way for conditional class binding i.e. if true then only
STYLE BINDING
EVENT BINDING
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";
}
for eg-> input krvaya and button pr click krne pr display krvaya log me .
logmessage(val){
console.log(val);
}
angular allows us to update a property and at the same time display the changes.
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 ->
<ng-template #thenblock>
<h2> Codevolution </h2>
</ng-template>
<ng-template #elseblock>
<h2> hidden </h2>
</ng-template>
ngSwitch DIRECTIVE -
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.
add ->
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)
or for alias name of property in test component we can replace the statement by ->
@Input('parentData') public name;
PIPES
allow us to transform data before displaying
number pipes
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
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 --->
output --->
Employee List
. andrew
. brandon
. christina
. elena
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();
}
}
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->
eg->
@Injectable({
providedIn: 'root'
})
this is important when the service have dependencies also.
component decorator tells that you might have dependencies.
observables->
eg- newspaper company
-> it is a sequence of items that arrive asynchronously over time.
HTTP call - single item
single item - HTTP response
RxJS
-Reactive Extensions for Javascript
-External library to work with observables
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>
<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>
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.
<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
</form>
</div>
<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>
Suppose we gotta add the address ... it has different subparts.. street,
city,state,pincode,etc.
So this can be done by ngModelGroup directive.
{
"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": ""
}
Following classes are applied by angular which can be used for validation.
{{name.className}} //display
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 -->
This pattern thing ensure the number should be of 10 digits only.We've done class
binding (bootstrap)
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.
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)".....>
FORM VALIDATION
We can add form validation in the <form> tag.
Step 1: Create the reference varibale to ngForm -->
<form #userForm= "ngForm">
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 PORT = 3000; //port no. that our express server will
run on
app.use(cors());
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);
});
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;
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"});
___________________________________________________________________________________
__________________
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
)*/
}
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.
<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> -->
('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>
**NOTE -> Select All Occurrences of Find Match {ctrl + shift + L} and {alt +
F3}
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 3: This validator is applied on the formGroup only ... i.e. on the
registrationForm .. not on the userName or password or confirmPassword.
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>
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"}