Components are the main building blocks for Angular applications. Each component consists of:
ng generate component <component-name><component-name>.component.ts<component-name>.component.html<component-name>.component.css<component-name>.component.spec.tsEvery component requires(!) a CSS selector. A selector instructs Angular to instantiate this component wherever it finds the corresponding tag in template HTML.
@Component({
selector: 'app-component-overview',
})
template: '<h1>Hello World!</h1>',templateUrl: './component-overview.component.html',styles: ['h1 { font-weight: normal; }']styleUrls: ['./component-overview.component.css']https://angular.io/guide/lifecycle-hooks#component-lifecycle
Responding to lifecycle event is possible by implementing interface of a lifecycle hook. Every such interface contains only one method. The interface is named usually like OnXyz and the corresponding method ngOnXyz(). For example:
@Directive({selector: '[appPeekABoo]'})
export class PeekABooDirective implements OnInit {
constructor(private logger: LoggerService) { }
// implement OnInit's `ngOnInit` method
ngOnInit() {
this.logIt('OnInit');
}
logIt(msg: string) {
this.logger.log(`#${nextId++} ${msg}`);
}
}
ngOnChanges()ngOnInit()ngOnChanges().ngOnInit()ngDoCheck()ngOnChanges() on every change detection run, and immediately after ngOnInit() on the first run.ngAfterContentInit()ngDoCheck().ngAfterContentChecked()ngAfterContentInit() and every subsequent ngDoCheck().ngAfterViewInit()ngAfterContentChecked().ngAfterViewChecked()ngAfterContentChecked().ngAfterViewInit() and every subsequent ngAfterContentChecked().ngOnDestroy()Use the ngOnInit() method to perform the following initialization tasks:
ngOnInit() is a good place for a component to fetch its initial data.An ngOnInit() is a good place for a component to fetch its initial data.
Angular provides several ways to clean up when an instance is destroyed.
ngOnDestroy()ngOnDestroy(), you can inject Angular's DestroyRef and register callback functions to be called when the enclosing context is destroyed. This can be useful for building reusable utilities that require cleanup.Sometimes it is necessary to use browser-only APIs to read or write the DOM. It is not possible with lifecycle events because they run during server-side rendering and pre-rendering. For this purpose Angular provides two functions (both are currently available only for developer preview):
These functions can be used for:
Spy exampleAngular calls the ngOnChanges() method of a component or directive whenever it detects changes to the input properties.
…
…
While the ngDoCheck() hook can detect when the hero's name has changed, it is an expensive hook. This hook is called with enormous frequency —after every change detection cycle no matter where the change occurred.
If you use this hook, your implementation must be extremely lightweight or the user experience suffers.
In Angular, a component's styles can be encapsulated within the component's host element so that they don't affect the rest of the application.
Following modes are available:
…
Example:
HeroChildComponent has two input properties, typically adorned with @Input() decorator:
import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'app-hero-child',
template: `
<h3>{{hero.name}} says:</h3>
<p>I, {{hero.name}}, am at your service, {{masterName}}.</p>
`
})
export class HeroChildComponent {
@Input() hero!: Hero;
@Input('master') masterName = '';
}
Using HerChildComponent in HeroParentComponent:
import { Component } from '@angular/core';
import { HEROES } from './hero';
@Component({
selector: 'app-hero-parent',
template: `
<h2>{{master}} controls {{heroes.length}} heroes</h2>
<app-hero-child
*ngFor="let hero of heroes"
[hero]="hero"
[master]="master">
</app-hero-child>
`
})
export class HeroParentComponent {
heroes = HEROES;
master = 'Master';
}
Use an input property setter to intercept and act upon a value from the parent.
The setter of the name input property in the child NameChildComponent trims the whitespace from a name and replaces an empty value with default text.
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-name-child',
template: '<h3>"{{name}}"</h3>'
})
export class NameChildComponent {
@Input()
get name(): string { return this._name; }
set name(name: string) {
this._name = (name && name.trim()) || '<no name set>';
}
private _name = '';
}
Using NameChildComponent:
import { Component } from '@angular/core';
@Component({
selector: 'app-name-parent',
template: `
<h2>Master controls {{names.length}} names</h2>
<app-name-child *ngFor="let name of names" [name]="name"></app-name-child>
`
})
export class NameParentComponent {
// Displays 'Dr. IQ', '<no name set>', 'Bombasto'
names = ['Dr. IQ', ' ', ' Bombasto '];
}
Detect and act upon changes to input property values with the ngOnChanges() method of the OnChanges lifecycle hook interface.
You might prefer this approach to the property setter when watching multiple, interacting input properties.
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-version-child',
template: `
<h3>Version {{major}}.{{minor}}</h3>
<h4>Change log:</h4>
<ul>
<li *ngFor="let change of changeLog">{{change}}</li>
</ul>
`
})
export class VersionChildComponent implements OnChanges {
@Input() major = 0;
@Input() minor = 0;
changeLog: string[] = [];
ngOnChanges(changes: SimpleChanges) {
const log: string[] = [];
for (const propName in changes) {
const changedProp = changes[propName];
const to = JSON.stringify(changedProp.currentValue);
if (changedProp.isFirstChange()) {
log.push(`Initial value of ${propName} set to ${to}`);
} else {
const from = JSON.stringify(changedProp.previousValue);
log.push(`${propName} changed from ${from} to ${to}`);
}
}
this.changeLog.push(log.join(', '));
}
}
Test that both input properties are set initially and that button clicks trigger the expected ngOnChanges calls and values. …
The child component exposes an EventEmitter property with which it emits events when something happens. The parent binds to that event property and reacts to those events.
The child's EventEmitter property is an output property, typically adorned with an @Output() decorator as seen in this VoterComponent:
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-voter',
template: `
<h4>{{name}}</h4>
<button type="button" (click)="vote(true)" [disabled]="didVote">Agree</button>
<button type="button" (click)="vote(false)" [disabled]="didVote">Disagree</button>
`
})
export class VoterComponent {
@Input() name = '';
@Output() voted = new EventEmitter<boolean>();
didVote = false;
vote(agreed: boolean) {
this.voted.emit(agreed);
this.didVote = true;
}
}
The parent VoteTakerComponent binds an event handler called onVoted() that responds to the child event payload $event and updates a counter.
import { Component } from '@angular/core';
@Component({
selector: 'app-vote-taker',
template: `
<h2>Should mankind colonize the Universe?</h2>
<h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
<app-voter
*ngFor="let voter of voters"
[name]="voter"
(voted)="onVoted($event)">
</app-voter>
`
})
export class VoteTakerComponent {
agreed = 0;
disagreed = 0;
voters = ['Dr. IQ', 'Celeritas', 'Bombasto'];
onVoted(agreed: boolean) {
if (agreed) {
this.agreed++;
} else {
this.disagreed++;
}
}
}
A parent component cannot use data binding to read child properties or invoke child methods. Do both by creating a template reference variable for the child element and then reference that variable within the parent template as seen in the following example.
The local variable #timer is a reference to the child component.
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
@Component({
selector: 'app-countdown-parent-lv',
template: `
<h3>Countdown to Liftoff (via local variable)</h3>
<button type="button" (click)="timer.start()">Start</button>
<button type="button" (click)="timer.stop()">Stop</button>
<div class="seconds">{{timer.seconds}}</div>
<app-countdown-timer #timer></app-countdown-timer>
`,
styleUrls: ['../assets/demo.css']
})
export class CountdownLocalVarParentComponent { }
The local variable approach is straightforward. But it is limited because the parent-child wiring must be done entirely within the parent template. The parent component itself (the parent class!) has no access to the child.
When the parent component class requires that kind of access, inject the child component into the parent as a ViewChild.
import { AfterViewInit, ViewChild } from '@angular/core';
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
@Component({
selector: 'app-countdown-parent-vc',
template: `
<h3>Countdown to Liftoff (via ViewChild)</h3>
<button type="button" (click)="start()">Start</button>
<button type="button" (click)="stop()">Stop</button>
<div class="seconds">{{ seconds() }}</div>
<app-countdown-timer></app-countdown-timer>
`,
styleUrls: ['../assets/demo.css']
})
export class CountdownViewChildParentComponent implements AfterViewInit {
@ViewChild(CountdownTimerComponent)
private timerComponent!: CountdownTimerComponent;
seconds() { return 0; }
ngAfterViewInit() {
// Redefine `seconds()` to get from the `CountdownTimerComponent.seconds` ...
// but wait a tick first to avoid one-time devMode
// unidirectional-data-flow-violation error
setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
}
start() { this.timerComponent.start(); }
stop() { this.timerComponent.stop(); }
}
A parent component and its children share a service whose interface enables bidirectional communication within the family.
The scope of the service instance is the parent component and its children. Components outside this component subtree have no access to the service or their communications.
...
…
For every Angular component you write, you can define not only an HTML template, but also the CSS styles that go with that template, specifying any selectors, rules, and media queries that you need.
One way to do this is to set the styles property in the component metadata. The styles property takes an array of strings that contain CSS code.
@Component({
selector: 'app-root',
template: `
<h1>Tour of Heroes</h1>
<app-hero-main [hero]="hero"></app-hero-main>
`,
styles: ['h1 { font-weight: normal; }']
})
export class HeroAppComponent {
/* . . . */
}
You should consider the styles of a component to be private implementation details for that component.
As component author, you can explicitly design a component to accept customization in one of four different ways.
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…