Angular 5 🅰️ Components — The Building Blocks
A component is the fundamental building block of every Angular application. It’s a class decorated with @Component that bundles three things together: the template it renders, the styles it applies, and the logic it runs. Every screen you see in Angular is a tree of components, starting from the root and branching down.
Key point: A component is a class, a template, and styles — bound together by the @Component decorator. The class holds the state and behavior; the template defines what the user sees; the styles scope the appearance. Everything in Angular UI starts here.
What a component is
A component has three parts that always travel together.
The class — a TypeScript class with properties and methods. This is where your data lives and where your logic runs.
The template — HTML with Angular syntax that tells Angular what to render. It can be inline or in a separate file.
The styles — CSS that applies only to this component’s template, scoped automatically by Angular.
The @Component decorator ties the three together. Without it, the class is just a class — Angular won’t know it’s a component.
Why components matter: Angular applications are trees of components. Each component owns a piece of the UI, holds its own state, and communicates with its parent and children. Building a UI means composing components — not writing one giant HTML file.
Creating a component
The CLI generates components with the correct structure:
ng generate component user-card
This creates four files — .ts, .html, .css, .spec.ts — and wires the component into the module or standalone imports.
The generated class:
import { Component } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
imports: [],
templateUrl: './user-card.component.html',
styleUrl: './user-card.component.css'
})
export class UserCardComponent {
}
Four things to notice:
selector— the tag name used in other templatesstandalone: true— modern Angular doesn’t require NgModuletemplateUrl— points to the HTML filestyleUrl— points to the CSS file
The class is empty for now — you fill it with properties and methods.
The template:
<p>user-card works!</p>
That’s the default. You replace it with real markup.
The styles:
Empty by default. Add component-scoped CSS here.
Why a decorator instead of a base class: The
@Componentdecorator carries metadata Angular reads at build time. It can define a template inline or by reference, and it can bring in other components the template uses. That’s much more flexible than inheritance.
The selector — using a component
The selector is the tag name that renders this component. Given selector: 'app-user-card', you use it like any HTML tag:
<app-user-card></app-user-card>
Angular replaces the tag with the component’s rendered template.
Selector styles:
| Form | Example | Use |
|---|---|---|
| Element | 'app-user-card' | Most common |
| Attribute | '[app-user-card]' | Directive-like |
| Class | '.app-user-card' | Rare |
The CLI uses the project’s prefix (app by default) to avoid collisions with HTML elements.
Why a prefix: Browsers reserve certain tag names. A prefix like
app-keeps your components distinct and clearly Angular-specific.
Templates — inline vs file
A component can define its template in two ways.
Inline template — the HTML lives in the decorator:
@Component({
selector: 'app-greeting',
template: `<h1>Hello, {{ name }}!</h1>`
})
export class GreetingComponent {
name = 'Alice';
}
File template — the HTML lives in a separate file:
@Component({
selector: 'app-greeting',
templateUrl: './greeting.component.html'
})
export class GreetingComponent {
name = 'Alice';
}
| Form | When to use |
|---|---|
| Inline | Very short templates (1–3 lines) |
| File | Anything larger |
The CLI defaults to file templates. Inline templates are fine for tiny components.
Why file templates by default: Editor support — syntax highlighting, autocomplete, and linting — is better in separate HTML files. Small components can use inline, but anything substantial belongs in a file.
The class — properties and methods
The class holds the state and behavior.
export class CounterComponent {
count = 0;
increment() {
this.count++;
}
decrement() {
this.count--;
}
reset() {
this.count = 0;
}
}
Every property is available in the template. Every method can be called from the template or bound to events.
Public by default — class members are accessible from the template. Use private or protected for anything the template shouldn’t touch.
TypeScript types — everything is typed. This is what makes templates type-checkable.
Why keep the class focused: A component should do one thing. State, methods, and template belong together, but if the class grows past ~200 lines, it’s probably doing too much. Split into child components or move logic into services.
Styles and view encapsulation
By default, Angular scopes component styles to that component’s template. A .title rule in one component doesn’t leak into another.
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrl: './header.component.css'
})
/* header.component.css */
.title {
color: red;
}
Only elements in header.component.html with class title get the red color. Other components can use .title freely.
View encapsulation modes:
| Mode | Behavior |
|---|---|
Emulated (default) | Scoped via attributes |
None | Global — leaks to the whole app |
ShadowDom | Native shadow DOM |
You can change it:
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrl: './header.component.css',
encapsulation: ViewEncapsulation.None
})
None means the styles apply globally. That’s useful for third-party CSS, but it can surprise you.
Why scoping matters: Without it, every component’s CSS would fight every other component’s. Scoping lets you name classes
.titleand.cardin many places without collision.
Component lifecycle
Components have a lifecycle. Angular creates them, updates them, and destroys them. You can hook into each phase with lifecycle methods.
| Hook | When it runs |
|---|---|
ngOnChanges | When an @Input changes |
ngOnInit | Once after the first ngOnChanges |
ngDoCheck | Every change detection cycle |
ngAfterContentInit | After projected content initializes |
ngAfterContentChecked | After every projection check |
ngAfterViewInit | After the component’s view initializes |
ngAfterViewChecked | After every view check |
ngOnDestroy | Just before the component is destroyed |
Example:
import { Component, OnInit, OnDestroy } from '@angular/core';
@Component({
selector: 'app-timer',
template: `<p>{{ seconds }}s</p>`
})
export class TimerComponent implements OnInit, OnDestroy {
seconds = 0;
private intervalId?: number;
ngOnInit(): void {
this.intervalId = window.setInterval(() => this.seconds++, 1000);
}
ngOnDestroy(): void {
if (this.intervalId) clearInterval(this.intervalId);
}
}
ngOnInit is the right place for setup — data fetching, subscriptions, initial state. ngOnDestroy is where you clean up — unsubscribe, clear timers, detach listeners.
Why
ngOnInitand not the constructor: The constructor runs before Angular sets up inputs.ngOnInitruns after inputs are set. Use the constructor only for dependency injection; do everything else inngOnInit.
Standalone vs module-based
Modern Angular defaults to standalone components — each one declares its own imports.
@Component({
selector: 'app-user-card',
standalone: true,
imports: [CommonModule, RouterLink],
templateUrl: './user-card.component.html'
})
export class UserCardComponent {}
Older Angular used NgModules — components were declared in a module and imported from there.
Standalone components:
- Have
standalone: true(or omit it in v19+ — standalone is the default) - Declare their own imports
- Don’t need to be listed in a module
Module-based components:
- Are declared in an
NgModule - Get imports from the module’s
importsarray
Standalone is the modern direction. New projects use it by default. Modules still work for legacy code and libraries that haven’t migrated.
Why standalone: A component declares exactly what it needs. There’s no shared module-level namespace, which makes dependencies explicit and tree-shaking more effective.
A complete example
A small component that fetches a user and displays a card.
The class:
import { Component, OnInit, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
interface User {
id: number;
name: string;
email: string;
}
@Component({
selector: 'app-user-card',
standalone: true,
imports: [CommonModule],
templateUrl: './user-card.component.html',
styleUrl: './user-card.component.css'
})
export class UserCardComponent implements OnInit {
@Input() userId!: number;
user?: User;
loading = false;
ngOnInit(): void {
this.loadUser();
}
loadUser(): void {
this.loading = true;
// In real code, fetch from a service
setTimeout(() => {
this.user = { id: this.userId, name: 'Alice', email: 'alice@example.com' };
this.loading = false;
}, 300);
}
}
The template:
<div class="card">
<p *ngIf="loading">Loading...</p>
<div *ngIf="user">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
</div>
</div>
The styles:
.card {
border: 1px solid #ccc;
padding: 1rem;
border-radius: 8px;
}
Using it:
<app-user-card [userId]="1"></app-user-card>
The parent passes userId, the child fetches and displays the user.
Why this shape: The class fetches and holds state. The template renders that state. The styles scope the appearance. The
@Inputaccepts data from the parent. Everything about “user card” lives in one folder.
Component communication
Parents and children talk through @Input and @Output.
@Input — parent sends data to child:
@Input() title!: string;
<app-card [title]="'Hello'"></app-card>
@Output — child sends events to parent:
@Output() clicked = new EventEmitter<void>();
onClick() {
this.clicked.emit();
}
<app-card (clicked)="handleClick()"></app-card>
Modern Angular also has signal-based input() and output() functions — covered in later chapters.
Why input/output: Components should be self-contained. They receive data through inputs and emit events through outputs. This keeps them reusable and testable.
Complete Example Session
# ============================================
# PART 1: GENERATE A COMPONENT
# ============================================
ng generate component greeting
# [ CREATE src/app/greeting/greeting.component.ts ]
# [ CREATE src/app/greeting/greeting.component.html ]
# [ CREATE src/app/greeting/greeting.component.css ]
# [ CREATE src/app/greeting/greeting.component.spec.ts ]
# ============================================
# PART 2: EDIT THE CLASS
# ============================================
cat src/app/greeting/greeting.component.ts
# [ import { Component } from '@angular/core'; ]
# [ ]
# [ @Component({ ]
# [ selector: 'app-greeting', ]
# [ standalone: true, ]
# [ imports: [], ]
# [ templateUrl: './greeting.component.html', ]
# [ styleUrl: './greeting.component.css' ]
# [ }) ]
# [ export class GreetingComponent { ]
# [ name = 'Alice'; ]
# [ } ]
# ============================================
# PART 3: EDIT THE TEMPLATE
# ============================================
cat src/app/greeting/greeting.component.html
# [ <h1>Hello, {{ name }}!</h1> ]
# ============================================
# PART 4: EDIT THE STYLES
# ============================================
cat src/app/greeting/greeting.component.css
# [ h1 { color: purple; } ]
# ============================================
# PART 5: USE THE COMPONENT
# ============================================
cat src/app/app.component.html
# [ <app-greeting></app-greeting> ]
# ============================================
# PART 6: ADD LIFECYCLE HOOKS
# ============================================
cat > src/app/timer/timer.component.ts << 'EOF'
import { Component, OnInit, OnDestroy } from '@angular/core';
@Component({
selector: 'app-timer',
standalone: true,
template: `<p>{{ seconds }}s</p>`
})
export class TimerComponent implements OnInit, OnDestroy {
seconds = 0;
private intervalId?: number;
ngOnInit(): void {
this.intervalId = window.setInterval(() => this.seconds++, 1000);
}
ngOnDestroy(): void {
if (this.intervalId) clearInterval(this.intervalId);
}
}
EOF
# ============================================
# PART 7: ADD AN INPUT
# ============================================
cat > src/app/card/card.component.ts << 'EOF'
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-card',
standalone: true,
template: `<h2>{{ title }}</h2><ng-content></ng-content>`
})
export class CardComponent {
@Input() title!: string;
}
EOF
# ============================================
# PART 8: ADD AN OUTPUT
# ============================================
cat > src/app/button/button.component.ts << 'EOF'
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-button',
standalone: true,
template: `<button (click)="onClick()">Click</button>`
})
export class ButtonComponent {
@Output() clicked = new EventEmitter<void>();
onClick(): void {
this.clicked.emit();
}
}
EOF
# ============================================
# PART 9: USE INPUTS AND OUTPUTS
# ============================================
cat > src/app/app.component.html << 'EOF'
<app-card title="Welcome">
<app-button (clicked)="handleClick()"></app-button>
</app-card>
EOF
# ============================================
# PART 10: SERVE AND VIEW
# ============================================
ng serve
# [ Local: http://localhost:4200/ ]
Quick Reference
Component Anatomy
| Part | Purpose |
|---|---|
| Class | State and behavior |
| Template | What to render |
| Styles | Scoped appearance |
@Component | Ties them together |
Decorator Metadata
| Field | Purpose |
|---|---|
selector | Tag name |
template / templateUrl | Inline or file template |
styles / styleUrl | Inline or file styles |
standalone | No NgModule needed |
imports | Components/directives/pipes used |
encapsulation | View encapsulation mode |
changeDetection | Default or OnPush |
Selector Forms
| Form | Example |
|---|---|
| Element | 'app-card' |
| Attribute | '[appCard]' |
| Class | '.app-card' |
Lifecycle Hooks
| Hook | When |
|---|---|
ngOnChanges | Input changes |
ngOnInit | After first changes |
ngDoCheck | Every CD cycle |
ngAfterContentInit | Content initialized |
ngAfterContentChecked | Content checked |
ngAfterViewInit | View initialized |
ngAfterViewChecked | View checked |
ngOnDestroy | Before destruction |
View Encapsulation
| Mode | Behavior |
|---|---|
Emulated | Scoped (default) |
None | Global |
ShadowDom | Native shadow DOM |
Inputs and Outputs
| Decorator | Direction |
|---|---|
@Input() | Parent → child |
@Output() | Child → parent |
CLI Command
| Command | Purpose |
|---|---|
ng g c NAME | Generate component |
ng g c NAME --inline-template | Inline template |
ng g c NAME --inline-style | Inline styles |
ng g c NAME --skip-tests | No spec |
ng g c NAME --flat | No folder |
Best Practices
✅ Do This:
// Use standalone components
@Component({ standalone: true }) // ✅
// Keep the class focused on one thing
export class UserCardComponent {} // ✅
// Use lifecycle hooks for setup/cleanup
ngOnInit() { ... }
ngOnDestroy() { clearInterval(this.id); } // ✅
// Use @Input for parent → child data
@Input() title!: string; // ✅
// Use @Output for child → parent events
@Output() clicked = new EventEmitter<void>(); // ✅
// Keep templates focused
// Split large templates into child components // ✅
// Scope styles — never use ::ng-deep unless needed
.card { ... } // ✅
// Use the CLI to generate components
ng g c user-card // ✅
❌ Don’t Do This:
// Don't put everything in one component
export class AppComponent { /* 500 lines */ } // ❌
// Don't do async work in the constructor
constructor() {
this.loadData(); // ❌ use ngOnInit
}
// Don't forget to clean up
ngOnInit() {
setInterval(() => {}, 1000); // ❌ leaks on destroy
}
// Don't access DOM directly
document.querySelector('.card'); // ❌ use ViewChild
// Don't use ::ng-deep for layout
::ng-deep .card { ... } // ⚠️ global styles
// Don't skip the decorator
export class UserCardComponent {} // ❌ not a component
// Don't mutate @Input objects
@Input() user!: User;
ngOnInit() { this.user.name = 'x'; } // ⚠️ affects parent
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
Forgot @Component | Not a component | Add decorator |
| Wrong selector | Component doesn’t render | Match tag name |
| Constructor for setup | Inputs not yet set | Use ngOnInit |
| No cleanup | Memory leaks | ngOnDestroy |
::ng-deep overuse | Global styles | Scope properly |
| Component too large | Hard to maintain | Split into children |
| Missing imports | Template errors | Add to imports array |
| Mutating inputs | Parent data changes | Treat as read-only |
Real-World Examples
1. Generate a component
ng g c user-card
Creates four files and wires them.
2. Edit the class
export class UserCardComponent {
name = 'Alice';
}
Add state.
3. Edit the template
<p>Hello, {{ name }}</p>
Bind state to view.
4. Edit the styles
p { color: purple; }
Scoped to this component.
5. Use the selector
<app-user-card></app-user-card>
Renders the component.
6. Add an input
@Input() userId!: number;
Parent can pass data.
7. Add an output
@Output() clicked = new EventEmitter<void>();
Child can notify parent.
8. Use lifecycle hooks
ngOnInit() { this.load(); }
ngOnDestroy() { this.cleanup(); }
Setup and cleanup.
9. Change view encapsulation
encapsulation: ViewEncapsulation.None
Global styles.
10. Inline template for small components
template: `<p>{{ msg }}</p>`
Small, single-line components.
Visual: Component Anatomy
┌──────────────────────────────────────────────┐
│ @Component │
│ │ │
│ ├──► selector: 'app-user-card' │
│ ├──► templateUrl: './...html' │
│ ├──► styleUrl: './...css' │
│ └──► standalone: true │
│ │
│ Class: UserCardComponent │
│ │ │
│ ├──► properties (state) │
│ └──► methods (behavior) │
│ │
│ Template: what to render │
│ Styles: how it looks │
│ │
└──────────────────────────────────────────────┘
Visual: Component Tree
┌──────────────────────────────────────────────┐
│ AppComponent │
│ │ │
│ ├──► HeaderComponent │
│ │ │
│ ├──► MainComponent │
│ │ ├──► UserListComponent │
│ │ │ ├──► UserCardComponent │
│ │ │ └──► UserCardComponent │
│ │ └──► SidebarComponent │
│ │ │
│ └──► FooterComponent │
│ │
└──────────────────────────────────────────────┘
Visual: Input and Output Flow
┌──────────────────────────────────────────────┐
│ Parent │
│ │
│ <app-child │
│ [data]="parentData" │
│ (event)="handleEvent($event)"> │
│ │
│ ──► data flows down via @Input │
│ ◄── events flow up via @Output │
│ │
└──────────────────────────────────────────────┘
Visual: Lifecycle Order
┌──────────────────────────────────────────────┐
│ constructor │
│ ▼ │
│ ngOnChanges (first) │
│ ▼ │
│ ngOnInit │
│ ▼ │
│ ngDoCheck │
│ ▼ │
│ ngAfterContentInit │
│ ▼ │
│ ngAfterContentChecked │
│ ▼ │
│ ngAfterViewInit │
│ ▼ │
│ ngAfterViewChecked │
│ │
│ (on destroy) │
│ ngOnDestroy │
│ │
└──────────────────────────────────────────────┘
Visual: View Encapsulation
┌──────────────────────────────────────────────┐
│ Emulated (default) │
│ │
│ .card { color: red; } │
│ │
│ Angular rewrites to: │
│ .card[_ngcontent-abc] { color: red; } │
│ │
│ Only this component's elements match │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ None │
│ │
│ .card { color: red; } │
│ │
│ Applied globally │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ ShadowDom │
│ │
│ Uses browser's native shadow DOM │
│ Real isolation, real boundaries │
│ │
└──────────────────────────────────────────────┘
Summary
| Concept | Meaning |
|---|---|
| Component | Class + template + styles |
@Component | Decorator tying them together |
| Selector | Tag name for the component |
| Template | What to render |
| Styles | Scoped appearance |
| Standalone | No NgModule needed |
| Lifecycle hooks | Setup and cleanup callbacks |
@Input | Parent → child data |
@Output | Child → parent events |
| View encapsulation | Style scoping mode |
Key takeaways:
- A component bundles a class, a template, and styles with the
@Componentdecorator - The selector is the tag name — the CLI prefixes it (
app-) to avoid collisions - Templates can be inline or in separate files — files are better for anything nontrivial
- The class holds state and behavior — properties and methods visible to the template
- Styles are scoped per component by default —
Emulatedencapsulation - Lifecycle hooks let you run code at specific points —
ngOnInitfor setup,ngOnDestroyfor cleanup - Standalone components are the modern default — they declare their own imports
@Inputbrings data in from the parent;@Outputsends events back out- Keep components focused — split large ones into child components
- Use
ng generate component— it wires everything up correctly
Remember: Components are the atoms of an Angular app. Class, template, styles — bundled by a decorator. They compose into a tree, talk through inputs and outputs, and clean up through lifecycle hooks. Learn to keep them small, focused, and self-contained, and every screen you build will be made of clear, reusable pieces.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!