| |

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 templates
  • standalone: true — modern Angular doesn’t require NgModule
  • templateUrl — points to the HTML file
  • styleUrl — 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 @Component decorator 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:

FormExampleUse
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';
}
FormWhen to use
InlineVery short templates (1–3 lines)
FileAnything 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:

ModeBehavior
Emulated (default)Scoped via attributes
NoneGlobal — leaks to the whole app
ShadowDomNative 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 .title and .card in 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.

HookWhen it runs
ngOnChangesWhen an @Input changes
ngOnInitOnce after the first ngOnChanges
ngDoCheckEvery change detection cycle
ngAfterContentInitAfter projected content initializes
ngAfterContentCheckedAfter every projection check
ngAfterViewInitAfter the component’s view initializes
ngAfterViewCheckedAfter every view check
ngOnDestroyJust 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 ngOnInit and not the constructor: The constructor runs before Angular sets up inputs. ngOnInit runs after inputs are set. Use the constructor only for dependency injection; do everything else in ngOnInit.


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 imports array

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 @Input accepts 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

PartPurpose
ClassState and behavior
TemplateWhat to render
StylesScoped appearance
@ComponentTies them together

Decorator Metadata

FieldPurpose
selectorTag name
template / templateUrlInline or file template
styles / styleUrlInline or file styles
standaloneNo NgModule needed
importsComponents/directives/pipes used
encapsulationView encapsulation mode
changeDetectionDefault or OnPush

Selector Forms

FormExample
Element'app-card'
Attribute'[appCard]'
Class'.app-card'

Lifecycle Hooks

HookWhen
ngOnChangesInput changes
ngOnInitAfter first changes
ngDoCheckEvery CD cycle
ngAfterContentInitContent initialized
ngAfterContentCheckedContent checked
ngAfterViewInitView initialized
ngAfterViewCheckedView checked
ngOnDestroyBefore destruction

View Encapsulation

ModeBehavior
EmulatedScoped (default)
NoneGlobal
ShadowDomNative shadow DOM

Inputs and Outputs

DecoratorDirection
@Input()Parent → child
@Output()Child → parent

CLI Command

CommandPurpose
ng g c NAMEGenerate component
ng g c NAME --inline-templateInline template
ng g c NAME --inline-styleInline styles
ng g c NAME --skip-testsNo spec
ng g c NAME --flatNo 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

PitfallProblemSolution
Forgot @ComponentNot a componentAdd decorator
Wrong selectorComponent doesn’t renderMatch tag name
Constructor for setupInputs not yet setUse ngOnInit
No cleanupMemory leaksngOnDestroy
::ng-deep overuseGlobal stylesScope properly
Component too largeHard to maintainSplit into children
Missing importsTemplate errorsAdd to imports array
Mutating inputsParent data changesTreat 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

ConceptMeaning
ComponentClass + template + styles
@ComponentDecorator tying them together
SelectorTag name for the component
TemplateWhat to render
StylesScoped appearance
StandaloneNo NgModule needed
Lifecycle hooksSetup and cleanup callbacks
@InputParent → child data
@OutputChild → parent events
View encapsulationStyle scoping mode

Key takeaways:

  • A component bundles a class, a template, and styles with the @Component decorator
  • 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 — Emulated encapsulation
  • Lifecycle hooks let you run code at specific points — ngOnInit for setup, ngOnDestroy for cleanup
  • Standalone components are the modern default — they declare their own imports
  • @Input brings data in from the parent; @Output sends 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!