Angular Components
Components define views, which are sets of screen elements that Angular can choose among and modify according to your program logic and data. Every application has at least one root component that connect a component hierarchy with DOM. Each component defines a class that contains application data and logic, and is associated with an HTML template that defines a view to be displayed in a target environment.
The @Component() decorator identifies the class immediately below it as a component, and provides the template and related component-specific metadata.
Decorators are functions that modify JavaScript classes.
There are following ways for cross-component communication:
There are number of ways for parent child communication:
The @Component() decorator identifies the class immediately below it as a component, and provides the template and related component-specific metadata.
Decorators are functions that modify JavaScript classes.
There are following ways for cross-component communication:
- using query params
- if there is parent and child then better to use @Input(), @Output(), EventEmitter()
- pass data through service using Observable.
There are number of ways for parent child communication:
[Refer]-https://angular.io/guide/component-interaction
@Input is used to pass data from parent to child and the @Output can be used when you want to pass data from the child to the parent.
In child.component.ts whatever names we define alongside @Input and @Output are used as component [inputpropertyName] and (outputEventName)
<app-child [receivedParentMessage]="messageToSendP" (messageToEmit)="getMessage($event)"></app-child>
In child.component.ts we have defined @Input receivedParentMessage and @Output messageToEmit = new EventEmitter<string>();
we can multiple Input and outputs(may be with different data types).
[Refer]-https://blog.hackages.io/angular-component-interaction-with-input-output-and-eventemitter-72526422b95c
*ngFor: trackBy is used in *ngFor directive. we pass an array of object to *ngFor which is drawn in DOM. If add/deletec/modify the array, normally the whole DOM is redrawn, which is not good for performance in case of big array. (*ngFor="let item of items; trackBy: trackbyFunction" trackbyFunction(index, item), in trackby function we return the proprty we want to track. So now the respective object is redrwan in DOM.
Template reference variables: Use simple hashtag(#) to create a reference to an element in a template(scope is limited to current template only).
<input #phone placeholder="phone number">
The scope for this variable is the entire HTML template in which the reference is defined. It can be also used in components and directives
In component used as
<app-hello #helloComp></app-hello>
@Input is used to pass data from parent to child and the @Output can be used when you want to pass data from the child to the parent.
In child.component.ts whatever names we define alongside @Input and @Output are used as component [inputpropertyName] and (outputEventName)
<app-child [receivedParentMessage]="messageToSendP" (messageToEmit)="getMessage($event)"></app-child>
In child.component.ts we have defined @Input receivedParentMessage and @Output messageToEmit = new EventEmitter<string>();
we can multiple Input and outputs(may be with different data types).
[Refer]-https://blog.hackages.io/angular-component-interaction-with-input-output-and-eventemitter-72526422b95c
*ngFor: trackBy is used in *ngFor directive. we pass an array of object to *ngFor which is drawn in DOM. If add/deletec/modify the array, normally the whole DOM is redrawn, which is not good for performance in case of big array. (*ngFor="let item of items; trackBy: trackbyFunction" trackbyFunction(index, item), in trackby function we return the proprty we want to track. So now the respective object is redrwan in DOM.
Template reference variables: Use simple hashtag(#) to create a reference to an element in a template(scope is limited to current template only).
<input #phone placeholder="phone number">
The scope for this variable is the entire HTML template in which the reference is defined. It can be also used in components and directives
In component used as
<app-hello #helloComp></app-hello>
It can also be used for accessing child component from parent. We can access the all the methods/members defined in app-hello Component like helloComp.method().
While in directive used differently as directives are already attributes
<form (ngSubmit)="onSubmit(myForm)" #myForm="ngForm">
Here ngForm is applied internally while we define form but in this case we have to do it like this.
[Refer]-https://blog.angulartraining.com/tutorial-the-magic-of-template-reference-variables-3183f0a0d9d1
safe navigation operator ( ? ): <p>The item name is: {{item?.name}}</p> If item is null, the view still renders but the displayed value is blank; you see only "The item name is:" with nothing after it.
non-null assertion operator(!): It serves the same purpose in an Angular template as ?. <p>The item's color is: {{item.color!.toUpperCase()}}</p> When the Angular compiler turns your template into TypeScript code, it prevents TypeScript from reporting that item.color might be null or undefined.
The $any() type cast function cast the expression to the any type. <p>The item's undeclared best by date is: {{$any(this).bestByDate}}</p>
Sometimes a binding expression triggers a type error during AOT compilation and it is not possible or difficult to fully specify the type. To silence the error, you can use the $any() cast function to cast the expression to the any type.
User Input:
Template: <input (keyup)="onKey($event)"><p>{{values}}</p>
Component: onKey(event: any) { // without type info
this.values += event.target.value + ' | ';
}
Here event is casted to any type so if we input 'abc', and backspaces to remove the value will be displayed as 'a | ab | abc | ab | a | |'.
To get the disered result it should be like
onKey(event: KeyboardEvent) { // with type info
this.values += (event.target as HTMLInputElement).value + ' | ';
}
We can use the reference variable to get bind user input like this
Template: <input #box (keyup)="0"><p>{{box.value}}</p>
Angular only binds something if it has something in event. In above case we bind it to '0'(to statisfy condition) so as to display the value changes.
Using reference variable(#) we no longer requires knowledge of the $event and its structure
The current state of the input box is lost if the user mouses away and clicks elsewhere on the page. To fix this issue, listen to both the keyup and the blur event.
Directives:
There are three types of directives in angular:
Components: directives with a template.
Structural directives: change the DOM layout by adding and removing DOM elements.
Attribute directives: change the appearance or behavior of an element, component, or another directive.
Attribute Directive:
Sample attribute directive looks like:
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appHighlight]' //use [] to define attribute directive
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@HostListener('mouseenter') onMouseEnter() {
this.highlight('yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
In template, <p appHighlight>Highlight me!</p>
ElementRef grants direct access to the host DOM element through its nativeElement property
The @HostListener decorator lets you subscribe to events of the DOM element that hosts an attribute directive, the <p> in this case.
<p appHighlight highlightColor="yellow">Highlighted in yellow</p>
<p appHighlight [highlightColor]="'orange'">Highlighted in orange</p>
If we want to pass the color from component
<p appHighlight [highlightColor]="color">Highlighted with parent component's color</p> OR
<p [appHighlight]="color">Highlight me!</p> (short syntex)
We can also set the default property as @Input() defaultColor: string;
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || this.defaultColor || 'red');
}
<p [appHighlight]="color" defaultColor="violet">Highlight me too!</p>
Structural Directives: * is used before name.
You can apply many attribute directives to one host element. You can only apply one structural directive to a host element.
Pipes: A pipe takes in data as input and transforms it to a desired output. You can also chain pipes like {{ birthday | date | uppercase}}
Custom pipe can be written as:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent?: number): number {
return Math.pow(value, isNaN(exponent) ? 1 : exponent);
}
}
Pure and impure pipes: There are two categories of pipes: pure and impure. Pipes are pure by default.
Pure Pipe: Angular executes a pure pipe only when it detects changes to input primitive value( value types string, boolean, number, symbol) or change in object reference(date, array, function, object). To know about reference [Refer]- https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0
It means for objects if we add new value, update values, change month etc. pipe will ignore these changes. The sample date object is used as:
{{ birthday | date:'fullDate' | uppercase}}, here bithday is an input object.
This may seem restrictive but it's also fast. An object reference check is fast—much faster than a deep check for differences—so Angular can quickly determine if it can skip both the pipe execution and a view update.
Impure Pipes: For setting impure pipes set pure: false like
@Pipe({
name: 'flyingHeroesImpure',
pure: false
})
An impure pipe is called often, as often as every keystroke or mouse-move. Angular calls impure pipes in almost every change-detection cycle
The impure AsyncPipe: The AsyncPipe accepts a Promise or Observable and maintains a subscription to the input Observable and keeps delivering values from that Observable as they arrive.
Lifecycle methods:
Hooks for the Component(in order): constructor --> ngOnChanges --> ngOnInit --> ngDoCheck --> ngOnDestroy(just before component destroyed)
ngDoCheck and ngOnChanges should not be implemented together on the same component.
Hooks for the Component’s Children: ngAfterContentInit --> ngAfterContentChecked --> ngAfterViewInit --> ngAfterViewChecked
<form (ngSubmit)="onSubmit(myForm)" #myForm="ngForm">
Here ngForm is applied internally while we define form but in this case we have to do it like this.
[Refer]-https://blog.angulartraining.com/tutorial-the-magic-of-template-reference-variables-3183f0a0d9d1
safe navigation operator ( ? ): <p>The item name is: {{item?.name}}</p> If item is null, the view still renders but the displayed value is blank; you see only "The item name is:" with nothing after it.
non-null assertion operator(!): It serves the same purpose in an Angular template as ?. <p>The item's color is: {{item.color!.toUpperCase()}}</p> When the Angular compiler turns your template into TypeScript code, it prevents TypeScript from reporting that item.color might be null or undefined.
The $any() type cast function cast the expression to the any type. <p>The item's undeclared best by date is: {{$any(this).bestByDate}}</p>
Sometimes a binding expression triggers a type error during AOT compilation and it is not possible or difficult to fully specify the type. To silence the error, you can use the $any() cast function to cast the expression to the any type.
User Input:
Template: <input (keyup)="onKey($event)"><p>{{values}}</p>
Component: onKey(event: any) { // without type info
this.values += event.target.value + ' | ';
}
Here event is casted to any type so if we input 'abc', and backspaces to remove the value will be displayed as 'a | ab | abc | ab | a | |'.
To get the disered result it should be like
onKey(event: KeyboardEvent) { // with type info
this.values += (event.target as HTMLInputElement).value + ' | ';
}
We can use the reference variable to get bind user input like this
Template: <input #box (keyup)="0"><p>{{box.value}}</p>
Angular only binds something if it has something in event. In above case we bind it to '0'(to statisfy condition) so as to display the value changes.
Using reference variable(#) we no longer requires knowledge of the $event and its structure
The current state of the input box is lost if the user mouses away and clicks elsewhere on the page. To fix this issue, listen to both the keyup and the blur event.
Directives:
There are three types of directives in angular:
Components: directives with a template.
Structural directives: change the DOM layout by adding and removing DOM elements.
Attribute directives: change the appearance or behavior of an element, component, or another directive.
Attribute Directive:
Sample attribute directive looks like:
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appHighlight]' //use [] to define attribute directive
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@HostListener('mouseenter') onMouseEnter() {
this.highlight('yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
In template, <p appHighlight>Highlight me!</p>
ElementRef grants direct access to the host DOM element through its nativeElement property
The @HostListener decorator lets you subscribe to events of the DOM element that hosts an attribute directive, the <p> in this case.
<p appHighlight highlightColor="yellow">Highlighted in yellow</p>
<p appHighlight [highlightColor]="'orange'">Highlighted in orange</p>
If we want to pass the color from component
<p appHighlight [highlightColor]="color">Highlighted with parent component's color</p> OR
<p [appHighlight]="color">Highlight me!</p> (short syntex)
We can also set the default property as @Input() defaultColor: string;
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || this.defaultColor || 'red');
}
<p [appHighlight]="color" defaultColor="violet">Highlight me too!</p>
Structural Directives: * is used before name.
You can apply many attribute directives to one host element. You can only apply one structural directive to a host element.
Pipes: A pipe takes in data as input and transforms it to a desired output. You can also chain pipes like {{ birthday | date | uppercase}}
Custom pipe can be written as:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent?: number): number {
return Math.pow(value, isNaN(exponent) ? 1 : exponent);
}
}
Pure and impure pipes: There are two categories of pipes: pure and impure. Pipes are pure by default.
Pure Pipe: Angular executes a pure pipe only when it detects changes to input primitive value( value types string, boolean, number, symbol) or change in object reference(date, array, function, object). To know about reference [Refer]- https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0
It means for objects if we add new value, update values, change month etc. pipe will ignore these changes. The sample date object is used as:
{{ birthday | date:'fullDate' | uppercase}}, here bithday is an input object.
This may seem restrictive but it's also fast. An object reference check is fast—much faster than a deep check for differences—so Angular can quickly determine if it can skip both the pipe execution and a view update.
Impure Pipes: For setting impure pipes set pure: false like
@Pipe({
name: 'flyingHeroesImpure',
pure: false
})
An impure pipe is called often, as often as every keystroke or mouse-move. Angular calls impure pipes in almost every change-detection cycle
The impure AsyncPipe: The AsyncPipe accepts a Promise or Observable and maintains a subscription to the input Observable and keeps delivering values from that Observable as they arrive.
Lifecycle methods:
Hooks for the Component(in order): constructor --> ngOnChanges --> ngOnInit --> ngDoCheck --> ngOnDestroy(just before component destroyed)
ngDoCheck and ngOnChanges should not be implemented together on the same component.
Hooks for the Component’s Children: ngAfterContentInit --> ngAfterContentChecked --> ngAfterViewInit --> ngAfterViewChecked
Component Styles:
The styles specified in @Component metadata apply only within the template of that component.
The styles in the style file apply only to this component. They are not inherited by any components nested within the template nor by any content projected into the component. You can add multiple state urls.
:host pseudo-class selector to target styles in the element that hosts the component.
:host-context() selector looks for a CSS class in any ancestor of the component host element, up to the document root. The following example applies a background-color style to all <h2> elements inside the component, only if some ancestor element has the CSS class theme-light.
:host-context(.theme-light) h2 {
background-color: #eef;
}
Any style with /deep/ , >>> and ::ng-deep(Deprecated, will be removed in future) applied becomes a global style. In order to scope the specified style to the current component and all its descendants, be sure to include the :host selector before ::ng-deep. If the ::ng-deep combinator is used without the :host pseudo-class selector, the style can bleed into other components.
The following example targets all <h3> elements, from the host element down through this component to all of its child elements in the DOM.
:host /deep/ h3 {
font-style: italic;
}
View encapsulation: Component CSS styles are encapsulated into the component's view and don't affect the rest of the application. To control how this encapsulation happens on a per component basis, you can set the view encapsulation mode in the component metadata like encapsulation: ViewEncapsulation.Native. We have following encapsulation modes:
1)ShadowDom view encapsulation: Angular will create Shadow DOM for the component. In the browser, when you examine source code, you will see a Shadow DOM has been created for the respective Component and the style is scoped to that(means style tag containing all the styles will be placed inside the shadow DOM).
2)Native view encapsulation: It is same as above but there has been some changes in Shadow DOM implementation.
3)Emulated view encapsulation (the default): Angular only emulates the Shadow DOM and does not create a real shadow DOM. Styles are put in head but angular will put random id like attribute and styles will be scoped to that like:
DOM: <hero-details _nghost-pmm-5>
<h2 _ngcontent-pmm-5>Mister Fantastic</h2>
<hero-team _ngcontent-pmm-5 _nghost-pmm-6>
<h3 _ngcontent-pmm-6>Team</h3>
</hero-team>
</hero-detail>
and Styles as:
[_nghost-pmm-5] {
display: block;
border: 1px solid black;
}
h3[_ngcontent-pmm-6] {
background-color: white;
border: 1px solid #777;
}
-Angular has generated _nghost attribute to component host _ngcontent attribute is added element inside it. Angular has also updated the CSS accordingly.
4)None: It means no encapsulation and styles are applied globally. When you will inspect the DOM you will find whatever styles you have included in the component are added in head section.
Comments
Post a Comment