Component
The component and template define a view.
A component is a special type of directive. The @Component()
decorator extends the @Directive()
decorator with template-oriented features.
A view that belongs to a component is called a host view.
You can change the structure of elements by inserting, moving, or removing nested views within their view containers.
The documentation generally refers to elements (ElementRef
instances), as distinct from DOM elements (which can be accessed directly if necessary).
Lifecycle Hooks
After creating a component/directive by calling its constructor, Angular calls the lifecycle hook methods in the following sequence at specific moments:
ngOnChanges()
ngOnInit()
ngDoCheck()
ngAfterContentInit()
ngAfterContentChecked()
ngAfterViewInit()
ngAfterViewChecked()
ngOnDestroy()
Directive
There are three kinds 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.
Structural Directives
Structural directives are responsible for HTML layout. They shape or reshape the DOM’s structure, typically by adding, removing, or manipulating elements.
1
<div *ngIf="hero" class="name"></div>
Internally, Angular translates the *ngIf
attribute into a <ng-template>
element, wrapped around the host element:
1
2
3
<ng-template [ngIf]="hero">
<div class="name"></div>
</ng-template>
<ng-template>
The <ng-template>
is an Angular element for rendering HTML. It is never displayed directly.
@Directive
Option | Description |
---|---|
selector | The CSS selector that identifies this directive in a template and triggers instantiation of the directive. |
inputs | Enumerates the set of data-bound input properties for a directive |
outputs | Enumerates the set of event-bound output properties. |
providers | Configures the injector of this directive or component with a token that maps to a provider of a dependency. |
exportAs | Defines the name that can be used in the template to assign this directive to a variable. |
queries | Configures the queries that will be injected into the directive. |
host | Maps class properties to host element bindings for properties, attributes, and events, using a set of key-value pairs. |
inputs
1
2
3
4
inputs: ['bankName', 'id: account-id'],
// ...
bankName: string;
id: string;
Equivalent to:
1
2
@Input() bankName: string;
@Input('account-id') id: string;
host
- Host Listeners
- Host Property Bindings
- Attributes
1
2
3
4
5
host: {
'(click)': 'onClick($event.target)',
'[class.valid]': 'valid',
}
// ...
Equivalent to:
1
2
3
4
5
@HostListener('click', ['$event.target'])
onClick(btn) {
console.log('button', btn, 'number of clicks:', this.numberOfClicks++);
}
@HostBinding('class.valid') get valid() { return this.control.valid; }
providers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Greeter {
greet(name:string) {
return 'Hello ' + name + '!';
}
}
@Directive({
selector: 'greet',
providers: [
Greeter
]
})
class HelloWorld {
greeter:Greeter;
constructor(greeter:Greeter) {
this.greeter = greeter;
}
}
exportAs
1
2
3
4
5
6
7
8
9
10
11
12
13
@Directive({
selector: 'child-dir',
exportAs: 'child'
})
class ChildDir {
}
@Component({
selector: 'main',
template: `<child-dir #c="child"></child-dir>`
})
class MainComponent {
}
queries
@Component
viewProviders
Defines the set of injectable objects that are visible to its view DOM children.
Different with providers
when use ng-content
.
1
2
3
4
<h1>
<app-title></app-title>
<ng-content></ng-content>
</h1>
Dependency Injection
The injector is responsible for creating service instances and injecting them into classes.
A provider tells an injector how to create the service.
The @Injectable()
is an essential ingredient in every Angular service definition.
You can configure injectors with providers at different levels of your app, by setting a metadata value in one of three places:
- In the
@Injectable()
decorator for the service itself. - In the
@NgModule()
decorator for an NgModule. - In the
@Component()
decorator for a component.
The @Injectable()
decorator has the providedIn
metadata option, where you can specify the provider of the decorated service class with the root
injector, or with the injector for a specific NgModule
.
The @NgModule()
and @Component()
decorators have the providers
metadata option, where you can configure providers for NgModule-level or component-level injectors.
Services are singletons within the scope of an injector.
Inject DOM element
1
2
3
4
5
6
7
8
9
10
import { ElementRef } from '@angular/core';
export class HighlightDirective {
private el: HTMLElement;
constructor(el: ElementRef) {
this.el = el.nativeElement;
}
}
NgModule
NgModules configure the injector and the compiler and help organize related things together.
An NgModule is a class marked by the @NgModule
decorator.
@NgModule
imports
- array tells Angular what other NgModules the current module needsdeclarations
- array tells Angular which components belong to that moduleexports
- The set of components, directives, and pipes declared in this NgModule that can be used in the template of any component that is part of an NgModule that imports this NgModuleentryComponents
- The set of components to compile when this NgModule is defined, so that they can be dynamically loaded into the viewproviders
- The set of injectable objects that are available in the injector of this module.
The declarations
array only takes declarables
. Declarables
are components
, directives
and pipes
.
DOM manipulation
You get a reference to Angular DOM abstractions by using ViewChild
query along with template variable references. The simplest wrapper around a DOM element is ElementRef
. For templates you have TemplateRef
that allows you to create an embedded view. Host views can be accessed on componentRef
created using ComponentFactoryResolver
. The views can be manipulated with ViewContainerRef
. There are two directives that make the manual process automatic: ngTemplateOutlet
— for embedded views and ngComponentOutlet
for host views (dynamic components).
@ViewChild
1
<span #tref>I am span</span>
1
@ViewChild("tref", {read: ElementRef}) tref: ElementRef;
ViewContainerRef
cannot be inferred and have to be asked for specifically in read parameter. Others, like ViewRef
cannot be returned from the DOM and have to be constructed manually.
ElementRef
Useful for accessing native DOM element.
1
2
3
4
class ElementRef<T> {
constructor(nativeElement: T)
nativeElement: T
}
obtain an instance of ElementRef associated with their host element through DI mechanism:
1
2
3
4
export class SampleComponent{
constructor(private hostElement: ElementRef) {
console.log(this.hostElement.nativeElement.outerHTML);
}
TemplateRef
1
2
3
<ng-template #tpl>
<span>I am span in template</span>
</ng-template>
1
2
3
4
5
6
7
8
export class SampleComponent implements AfterViewInit {
@ViewChild("tpl") tpl: TemplateRef<any>;
ngAfterViewInit() {
let elementRef = this.tpl.elementRef;
console.log(elementRef.nativeElement.textContent);
}
}
When you define a template you can have input parameters specified through let-paramname
:
1
2
3
<template [paForOf]="getProducts()" let-item='item'>
<span></span>
</template>
1
this.container.createEmbeddedView(this.template, {item: {name: 'John'}}`
1
this.container.createEmbeddedView(this.template, { $implicit: this.dataSource[i] });
If the input property is specified like this let-item
without second part =something
, the embedded view treats it as let-item=$implicit
so you have to pass a context object with $implicit
property.
https://stackoverflow.com/questions/46910752/what-is-createembeddedview-context-parameter-in-angular
ViewRef
Angular supports two types of views:
- Embedded Views which are linked to a
Template
- Host Views which are linked to a
Component
Creating embedded view
With TemplateRef
:
1
let view = this.tpl.createEmbeddedView(null);
With ViewContainerRef
:
1
this.vc.createEmbeddedView(this.tpl);
Creating host view
1
2
3
4
5
6
constructor(private injector: Injector,
private r: ComponentFactoryResolver) {
let factory = this.r.resolveComponentFactory(ColorComponent);
let componentRef = factory.create(injector);
let view = componentRef.hostView;
}
Don’t forget that components that are instantiated dynamically must be added to EntryComponents
of a module or hosting component.
ViewContainerRef
Represents a container where one or more views can be attached.
Usually, a good candidate to mark a place where a ViewContainer should be created is ng-container
element.
1
@ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;
ViewContainer provides a convenient API for manipulating the views:
1
2
3
4
5
6
7
8
9
10
class ViewContainerRef {
...
clear() : void
insert(viewRef: ViewRef, index?: number) : ViewRef
get(index: number) : ViewRef
indexOf(viewRef: ViewRef) : number
detach(index?: number) : ViewRef
remove(index?: number): void
move(viewRef: ViewRef, currentIndex: number) : ViewRef
}
While the remove
method destroys the view so it can’t be re-attached later, the detach
method preserves it to be re-used in the future which is important for optimization.
ViewContainer also provides API to create a view automatically:
1
2
3
4
5
6
7
8
class ViewContainerRef {
element: ElementRef
length: number
createComponent(componentFactory...): ComponentRef<C>
createEmbeddedView(templateRef...): EmbeddedViewRef<C>
...
}
ngTemplateOutlet
1
2
3
4
5
6
<span>I am first span</span>
<ng-container [ngTemplateOutlet]="tpl"></ng-container>
<span>I am last span</span>
<ng-template #tpl>
<span>I am span in template</span>
</ng-template>
ngComponentOutlet
1
<ng-container *ngComponentOutlet="ColorComponent"></ng-container>
https://hackernoon.com/angular-pro-tip-how-to-dynamically-create-components-in-body-ba200cc289e6
Exploring Angular DOM manipulation techniques using ViewContainerRef
@ContentChild
1
<ng-content></ng-content>
1
2
3
4
5
6
7
8
9
10
11
12
<div class="card">
<div class="card-header">
</div>
<!-- single slot transclusion here -->
<ng-content></ng-content>
<div class="card-footer">
</div>
</div>
1
2
3
4
5
6
7
8
9
<card header="my header" footer="my footer">
<!-- put your dynamic content here -->
<div class="card-block">
<h4 class="card-title">You can put any content here</h4>
<p class="card-text">For example this line of text and</p>
<a href="#" class="btn btn-primary">This button</a>
</div>
<!-- end dynamic content -->
<card>
https://scotch.io/tutorials/angular-2-transclusion-using-ng-content
Form
Angular provides two different approaches to handling user input through forms: reactive and template-driven. Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes.
Both reactive and template-driven forms share underlying building blocks.
-
FormControl
tracks the value and validation status of an individual form control. -
FormGroup
tracks the same values and status for a collection of form controls. -
FormArray
tracks the same values and status for an array of form controls. -
ControlValueAccessor
creates a bridge between Angular FormControl instances and native DOM elements.
NgForm
Angular automatically creates and attaches an NgForm
directive to the <form>
tag.
- Each input element has an
id
property that is used by the label element’s for attribute to match the label to its input control.- Each input element has a
name
property that is required by Angular forms to register the control with the form.
ControlValueAccessor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import { Component, OnInit, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-date-picker',
templateUrl: './date-picker.component.html',
styleUrls: ['./date-picker.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DatePickerComponent),
multi: true
}
]
})
export class DatePickerComponent implements OnInit, ControlValueAccessor {
val: string = '';
onChange = Function.prototype;
onTouched = Function.prototype;
get value() {
return this.val
}
set value(val){
this.val = val
this.onChange(val)
}
constructor() { }
ngOnInit() { }
writeValue(value: string): void {
this.value = value
}
registerOnChange(fn: () => {}): void {
this.onChange = fn;
}
registerOnTouched(fn: () => {}): void {
this.onTouched = fn;
}
}
Subject
1
2
3
export class MyService {
public mysubject : Subject = new Subject();
}
1
myservice.mysubject.next("new value");
1
2
3
this.myservice.mysubject.subscribe( (value) => {
//insert your code here
});
Routing
In summary, you want to delay rendering the routed component until all necessary data have been fetched.
You need a resolver
.
i18n
Angular simplifies the following aspects of internationalization:
- Displaying dates, number, percentages, and currencies in a local format.
- Preparing text in component templates for translation.
- Handling plural forms of words.
- Handling alternative text.
When internationalizing your app, you need to do thorough testing to make sure UI components behave as expected even when their contents vary greatly in content size.
You need to build and deploy a separate version of the app for each supported language.
Pluralzation
1
<span i18n>Updated {minutes, plural, =0 {just now} =1 {one minute ago} other {{{minutes}} minutes ago}}</span>
- The first parameter is the key. It is bound to the component property (minutes), which determines the number of minutes.
- The second parameter identifies this as a plural translation type.
- The third parameter defines a pluralization pattern consisting of pluralization categories and their matching values.
Workspace Configuration
A file named angular.json
at the root level of an Angular workspace provides workspace-wide and project-specific configuration defaults for build and development tools provided by the Angular CLI.
Path values given in the configuration are relative to the root workspace folder.
- projects : Contains a subsection for each project (library or application) in the workspace, with the per-project configuration options.
Testing
CheatSheet
Glossary
Debug
1
ng.probe($0).componentInstance