Angular 2+
Html – JS Dev vs Angular Dev
Earlier we would keep these separate. We will manage the DOM via JS. So on click of a button, we will invoke a particular JS –which might validate form inputs.
Angular works in component based approach, we think in terms of components. So page is divided into components and each component can have other components inside it. The main component is the root-component.
In angular, we will define form as one component and all the JS is encapsulated inside it. So the components are self-sufficient.
How does angular start?
The index.html might contain certain elements <root-component> etc. The angular read the file main.ts [the first typescript to be called]. In main.ts we bootstrap the root component’s module. The root component’s module declares a root-component. The root-component’s ts file mentions its selector. So angular understands its selector as the element.
Node.js – runtime environment made on Chrome’s V8 JS engine. IT can interpret the code written in angular or other JS frameworks.
Test = node -v
NPM – node package manager manages versions of angular modules . It works on package.json which contains the list of dependencies and libraries needed by the project. Similar to pom.xml maven for java, however npm works on git only.
NPM gets installed with Node installation
After NPM installs we install CLI(ng) from NPM
Npm install @angular/cli [@group/package
Test = ng -v
Creation of new project
ng new <project-name> – this creates new files for the project and dependencies for the needed by it using NPM. It will create bare-bones project with some files.
Angular bootstraps app-root component which needs to be cleaned/ or a new component created in its place.
Creation of new Component
ng generate component <component-name>
Angular Component contains
- Html – view
- Css – view
- Ts – backing logic
- Spec.ts – unit testing
Decorators – Everything that begins with @. @Component, @NgModule, @Injectable
To declare a .ts as a component
@Component {
selector : “app-root” // the tag for this component
templateUrl : “./app.html” // relative path
styleUrls: “./app.css” // relative path
}
When you add a component, it creates 4 files with a folder and changes module.ts file.
In a component – template or templateUrl is mandatory.
selector : “app-root” – allows to be used <app-root></app-root>
selector : [app-root] – allows to be used <div app-root></div>
selector : .app-root – allows to be used <div class=”app-root”></div>
styleUrls can take multiple css files or one can give it inline with styles
DATA Binding
One way binding / a. String Interpolation – {{ }}
ts file -> html
When the value of the variable changes in .ts/data file, it is reflected in view.
One way binding / b. Property Binding – []
ts file -> html
Used to set properties in ts file to control dom elements in view.
<button [disabled]=”isDisabled”>
isDisabled should be a boolean in ts file.
String interpolation to be used when you want to display a text, property binding when you want to control element behavior using ts attribute.
You cannot mix both
One way binding / c. Event binding
The <button click=”funct()” > text </button> behaves as browser click.
The <button (click)=”funct()” > text </button> behaves as angular click and it will find the function in your component.
One can also send funct( $event ).
Two way binding – ngModel
In case we want to update value of variable two ways -i.e data in ts file and UI. It uses the syntax of banana in a box [(ngModel)] = “variableName”
To use this, we need to import FormModule from angular
Aysnc refresh a value
Use JS API – setInterval ( function(), interval )
setInterval ( () -> this.date = new Date().toDateString() , 1000 )
This ensures that the value date variable changes every second which is defined in the component.
{{ myFunc(abc, 2) }} – it can also call functions and display its values.
Directives – Instructions for the DOM
Structural Directives – which have * before them.
*ngIf – if condition for JS
<div *ngIf = “items.length>2”> </div>
*ngFor – to loop over elements/ arrays in JS.
example <p *ngFor=“let item of items” > {{item.element}} </P>
The star * is only to indicate the angular interpreter that it needs to transform this into a code that uses string interpolation, property binding which angular understands.
The *ngIf block converts to ng-template block with [ngIf] i.e property binding.
Attribute Directives
ngStyle which is a attribute directive used as a property binding. It can help to give a dynamic style to an attribute.
<p [ngStyle]=”backgroundColor: myFunc()”>
ngClass – similar to above but itnstead of adding css styles, it adds css classes dynamically.
<p [ngClass]=”my-class: myFunc()”>
Intercommunication between components
- Parent to child communication
Giving inputs to the components
<app-image src=”./svg/icon.gif” > </app-image>
In the image-component.ts
@Component {
selector : “app-root” // the tag for this component
templateUrl : “./app.html” // relative path
styleUrls: “./app.css” // relative path
}
export class image {
@Input(‘src’) filePath : string;
…
}
‘src’ is an alias – optional.
Input values are populated in ngOnIt() which is called post constructor.. If you try to read the value in constructor, it won’t fetch anything,
The above method simply works for string. In case we want to pass an object to a component?
We will change the syntax to
<app-image [src]=”myObj” > </app-image>
Here, we have used src in square bracket – property binding, this tell angular to take myObj as an object reference and not as a string.
b. Child to Parent Communication
Giving output / Emitting event from component
in Child component
@Output(‘eventNameAlias’) someEventName: new EventEmitter<MyObj>;
void fun () {
someEventName.emit();
}
In Parent’s html
<child-component (‘eventNameAlias’) = “parentsFunction($event)” >
Attribute vs Structural Directive
Attribute Directive – change or affect the element on which they are user
Structural Directive – They begin with * for demarcating. Affect the whole DOM.
Making one’s own directive – ng generate directive <name>
a. using just ElementRef approach
@Directive({
selector: ‘[appBasicHighlight]’
})
export class BasicHighlightDirective implements OnInit {
constructor(private elementRef: ElementRef) {
}
ngOnInit() {
this.elementRef.nativeElement.style.backgroundColor = ‘green’;
}
}
It is not considered good to change the elements directly. The DOM should be changed by angular’s API.
Because some of these styles may not be available in web services etc.
b. Using Renderer2 and ElementRef
constructor(private elRef: ElementRef, private renderer: Renderer2) { }
ngOnInit() {
this.renderer.setStyle(this.elRef.nativeElement, ‘background-color’, ‘blue’);
}
We are using Angular API’s to change DOM
c. Using @HostBinding and @HostListener
@HostBinding – simply binds/sets the value to the DOM
@HostListener – reacts to the known events in angular and sets the value in DOM
@HostBinding(‘style.backgroundColor’) backgroundColor: string;
@HostListener(‘mouseenter’) mouseover(eventData: Event) {
this.backgroundColor = this.highlightColor;
}
backgroundColor, highlightColor can be set using @input / property binding.
View Encapsulation
This is Angular’s way of emulating the Shadom DOM. In this, a style if applied to a component remains local to it.
There are three options
@Component({
selector:
templateUrl:
styles: ‘
encapsulation: ViewEncapsulation.None
})
- ViewEncapsulation.None – No Shadow DOM at all. Global styles
- ViewEncapsulation.Emulated – No Shadow DOM but style encapsulation emulation.
- ViewEncapsulation.Native – Native Shadow DOM with all it’s goodness.
Note – not all browsers support native dom.
Local reference
a. One can refer the Dom element with #<name>
<div class=”col-md-9″>
<input type=”text” class=”form-control” trim #giftName />
</div>
<div class=”row”>
<button class=”btn” (click)=”onAddGift(giftName)”>Add Gift</button>
</div>
onAddGift(giftName: HTMLInputElement) {
this.userGiftName = giftName.value;
}
b. @ViewChild – make an element in ts file of type ElementRef.
<div class=”col-md-9″>
<input type=”text” class=”form-control” trim #nickName />
</div>
<div class=”row”>
<button class=”btn” (click)=”onAddGift(nickName)”>Add Gift</button>
</div>
onAddGift(nickName: HTMLInputElement) {
..
this.userNickName = nickName.nativeElement.value;
.
}
@ViewChild(‘nickName’) nickName : ElementRef;
Content Projection
To write the content between the components.
<myComponent>
<p> hii… this would be ignored</p>
</myComponent>
To make sure it comes change the mycomponents templates with <ng-content></ng-content>
This will place the content between the braces at that location where the tag is present.One cna also enable <ng-content select=”div”> to take up only div elements.
Styling – Local and Global
Component.css can have common style names as of other components. The angular can handle it. So component 1 can have style as “header” with some css properties and component 2 can also have “header” style with other properties. They will not interfere.
In order to have global styles, application specific styles – use styles.css in src folder.
Modules
Are basically a collection of components, services.
ng generate module <name> -> creates a folder with name and name.module.ts file
To generate components inside this module
ng generate component MyModule/MyApp
This will add this component to the declarations parts of the module file.
To use a module inside another module
- Edit AppModule and add import for MyModule
@NgModule {
Imports :[ CommonModule, MyModule]
}
- To Use a component declared in MyModule in AppModule
Edit MyModule and add exports for MyAppComponent
@NgModule {
Exports: [MyApp ]
}
Import statement on top of the file is for the typescript compiler. Needed to link types.
Imports in @NgModule decorators is to tell angular which other modules can be used.
Service
ng generate service <MyService>
creates two files
service.ts
service.spec.ts
@Injectable decorator tells it is a service
To use a service in a module
@NgModule {
Providers: [ MyService ]
}
To inject this service into a component, you need to change the constructor of the component
MyComponent {
..
constructor (private serv : MyService ) { }
This will automatically inject MyService in MyComponent.
It is same as
private serv :MyService ;
constructor ( serv : MyService ) {
this. serv = serv;
}
Services are loaded in a common pool of injection context. So they are not bound to any particular module or component unlike components which are bound to a module which imports them.
So a service can be declared in any component and it can be used anywhere.
Scope of a Service
Services are injected using a hierarchical injector in angular.
Example. A has two children
a—-b, a—c
1. If the provider’s array of each of the components will have the service name then all will be using a new instance of service [similar to prototype scope in spring ].
2. If component A has service then all its children will get the same service instance. So if only the root component declares the provider for service, then it will be singleton. [the default in spring]
Injecting Service into Another Service
A service can be injected into another service if and if we use @Injectable() over it.
@injectable() is to be used on the service – which needs other services injected into it.
Angular 6+ Services
Instead of adding a service class to the providers[] array in AppModule , you can set the following config in @Injectable() :
- @Injectable({providedIn: ‘root’})
- export class MyService { … }
This is exactly the same as:
- export class MyService { … }
and
- import { MyService } from ‘./path/to/my.service’;
- @NgModule({
- …
- providers: [MyService]
- })
- export class MyService { … }
Using Services for InterComponent Communication
Earlier we have seen using parent child, child parent communication. An alternative is service.
Let’s say if a singleton service exists and we emit an event on its property. This can later be subscribed by some other component using the same service.
this.myservice.subscribe ( something to do );
Making a REST call
Import HTTPClientModule in app-root.
User DI to obtain HTTPClient
constructor (private http : HTTPClient ) { }
let response = http.get(“url”);
The response here is not a simple json object. It is an Observable which is an object that handles async calls.
response.subscribe( () -> console.log(“obtained”) )
or
response.subscribe( (json) -> console.log(json) )
So whenever we obtain the response from a web service we execute this function.
Building
Ng build –prod
Routing
Angular works on Single Page Application
Ng new <routing-component-name> –routing
You need to define path , component
a default can also be defined
“*” can be a path for component to open an error page.
<router-outlet></router-outlet> loads the page at that required location.
For children – sub routing
{path : “settings”, component : ABCComponent
Children {
path : “profile”, component : ProfileComponent // setttings/profile
path : “security”, component : SecurityComponent // settings/security
}
<a href=”/path”> – will refresh the entire page
<a routerLink = “/path” > – behaves as a SPA.
Type of Paths
./ or direct – relative path
/something – absolute path
Styling Routes
In the tag that encloses <a> tag one can use
<li role=”presentation”
routerLinkActive=”active”
[routerLinkActiveOptions]=”{exact: true}”>
<a routerLink=”/”>Home</a>
</li>
First option routerLinkActive sets the active style class and the other is used to set it to be true for exact match for absolute path. Otherwise all links that start with “/” will be made active.
Navigating Programmatically
- Absolute Path
Inject Router in constructor
onLoadServer() {
this.router.navigate([‘/servers’]);
}
- Relative Path – it needs the active routes information
Inject Router and ActivatedRoute in constructor
this.router.navigate([‘servers’] , {relativeTo: this.activatedRoute} );
Passing Params Dynamically
Passing params
{ path: ‘servers/:id’, component: ServerComponent)
Retrieving Params
Inject ActivatedRoute
this.activatedroute.snapshot.params[‘id’]
Retrieving Params Reactively
If one of the same components tries to click on the link that tries to open the same component again – angular wouldn’t reload the component.
However, in order to reload/re-instantiate the component one can subscribe to the changes in the params in ngOnInit().
this.paramsSubscription = this.route.params
.subscribe( (params: Params) => {
this.user.id = params[‘id’]; }
However, also unsubscribe the event on ngOnDestroy()
Passing Query Params and Fragments
- using <a> tag
<a [routerLink]=”[‘/servers’, server.id]”
[queryParams]=”{allowEdit: server.id === 3 ? ‘1’ : ‘0’}”
fragment=”loading”>
{{ server.name }}
</a>
b. using function
this.router.navigate([‘/servers’, id, ‘edit’], {queryParams: {allowEdit: ‘1’}, fragment: ‘loading’});
/server/1/edit?allowEdit=1#loading
Retrieving Query Params and Fragment
Inject ActivatedRoute
this.activatedroute.snapshot.queryparams[‘allowEdit’]
Retrieving Params Reactively
Similar to params , one can subscribe to the changes in the query params in ngOnIt().
Note : the params retrieved from url are in string. So if you consider passing them make sure to typecast them.
Preserving or merging query params on click of new link
queryParamsHandling: “preserve” – lets them remain same
queryParamsHandling: “merge”- merges any new params in the link
Child Routes
In case there are multiple components inside a component which are its children. So one can give routing in this manner.
{ path: “” , component : HeadComponent, children : {
{ path : “” , component : ChildComponent }
} } Note : the path here needs to be relative one as it is a child’s path
Add <router-outlet> to HeadComponent instead of ChildComponent’s selector tag. This will load only child of this component.
Redirecting to non found page
{ path “not-found” , redirectTo : PageNotFoundComponent },
{ path “**” , redirectTo :” /not-found” }
** is whatever path angular doesnt understand.
redirectTo – changes the url to point to this
pathMatch: ‘full’
Angular uses prefix matching by default. this can cause issues because ” will match every URL.
Router Guards
1. CanActivate Guard – To block access to certain URL’s which do not have the permission to access the resource.
Steps
a. Implement CanActivate in a class(this is called a Guard class) and implement the method canActivate() which returns a true or false.
b. Invoke a service inside this method to decide on the access.
c. Add this class to providers [because it acts a service]
d. In const of routes, add canActivate property and name the Guard class in array. There can be many Guards
path: ‘artist/:artistId’, component: ArtistComponent, canActivate: [AlwaysAuthGuard]
2. CanActivateChild Guard
For child routes – same steps only a different interface CanActivateChild needs to be implemented and different property
path: ‘artist/:artistId’, component: ArtistComponent, canActivateChild: [AlwaysAuthChildGuard]
3. CanDeactivate Guard – suppose a user is changing form data and before saving, user tries to navigate away. In this scenario we can use CanDeactivate guard which will deactivate the route and open a Dialog Box to take user confirmation. This Guard is usally only a single one for application.
It works on Command Design pattern
a. Create a new interface which has only a canDeactivate() method that returns either an observable, promise or boolean.
b. Make a new Guard class which extends CanDeactivate<T>. Above new interface will act as a generic type to CanDeactivate<your new Interface type here>
c. In the class we will call the component’s canDeactivate method.
d. Add this class to providers [because it acts a service]
e. Any class which needs to be Guarded needs to implement CanDeactivate<Component>.
f. In const of routes, add canActivate property and name the Guard class in array. There can be many Guards
path: ‘artist/:artistId’, component: ArtistComponent, canDeactivate: [DeActivateGuard]
Passing data to a route can be done statically via (data : “string” in routes) and dynamically by a resolver ( intermediate code to be executed when a link has been clicked and before a component is loaded)
Observables
Observables are data sources – could be events, Http requests, Triggers in code (All of which happen at a random time so its async in nature).
These need to be handled by angular. An Observer handles them. An observer has three parts – normal execution, error situation , completion of execution.
So when you subscribe to a variable – > the variable is itself an Observable and everything inside subscribe( observer: Observer ) is an observer.
myobserver : Subscription = abc.subscribe()
onDestroy() {
this.myobserver.unsubscribe();
}
Observables need to be unsubscribed if they are created by the user.. Angular ones are automatically taken care of.
We pass values to an observer using observer.next( //some value here );
Subjects
They act as both observer and observable. They can be effective for communication between components. They can replace EventEmitter
Example
A simple class lets say MyService has an object on subject.
Two components A and B inject this service.
A can pass values to the subject.
B can subscribe and read those values.
Pipe
Transforms the output without changing the property
{{name | uppercase }}
You can chain pipes.
You can make a custom pipe too by implementing PipeFilter interface or using ng generate pipe <name> command. Each pipe is initially impure (won’t react on changes if the value has changed). It can be made pure with a performance hit.
You can have async pipes