Skip to content

Angular Components

🎯 Hands-On Module: This is where Angular development gets practical! You'll create your first component and learn how components work by building real examples. Follow along with the exercises and experiment with the code.

What are Components?

Components are the fundamental building blocks of Angular applications. Think of them as custom HTML elements that you create to build your user interface. A component controls a portion of the screen through its template and manages the data and behavior for that view.

In simple terms: A component is like a reusable piece of UI that combines:

  • Logic (what it does)
  • Template (how it looks)
  • Styles (how it's styled)

Component Anatomy

Every Angular component consists of:

  1. TypeScript Class - Contains the component logic (properties, methods)
  2. HTML Template - Defines the view structure (what you see)
  3. CSS Styles - Defines the appearance (colors, fonts, layout)
  4. Metadata - Tells Angular how to process the component (@Component decorator)

🛠️ Step 0: Setting Up Our Demo Project

Before we start creating components, let's set up a clean demo project to work with.

Create the Demo Project

cd /path/to/your/workspace
ng new angular-module4-demo --routing --style=css --skip-git
cd angular-module4-demo

Clean Up the Default Template

The default Angular template has a lot of content. Let's replace it with a minimal setup:

  1. Open src/app/app.html and replace ALL content with within the placeholder with the following:
<div class="app-container">
  <h1>Angular Module 4 Demo</h1>
</div>

Your file should now look like this:

<main class="main">
  <div class="content">
    <h1>Angular Module 4 Demo</h1>
  </div>
</main>

<router-outlet />
  1. Open src/app/app.css and add minimal styling:
.main {
  margin: auto 0;
}

.content {
  padding: 20px;
  font-family: Arial, sans-serif;
}

h1 {
  color: #2c3e50;
  text-align: center;
}
  1. Test your setup:
ng serve

Visit http://localhost:4200 - you should see a clean page with "Angular Module 4 Demo" as the title.

You should see something like this:

Angular Module 4 Demo

🛠️ Hands-On: Creating Your First Component

Let's create a practical component step by step! We'll build a greeting component that displays a personalized message.

Step 1: Generate the Component

Now let's create our first custom component! A greeting component that displays a simple message.

Open your terminal in the demo project and run:

cd angular-module4-demo # if not already in the project directory
ng generate component greeting
# or shorthand:
ng g c greeting

What this creates in Angular 20:

  • src/app/greeting/greeting.ts - Component class
  • src/app/greeting/greeting.html - Template
  • src/app/greeting/greeting.css - Styles
  • src/app/greeting/greeting.spec.ts - Unit tests

Note: Angular 20 uses simplified file naming (no .component in filenames) and standalone components by default.

Step 2: Create a Simple Greeting Component

Let's keep it minimal! Add the message property to the component class to display it in the template.

src/app/greeting/greeting.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-greeting',
  imports: [],
  templateUrl: './greeting.html',
  styleUrl: './greeting.css'
})
export class Greeting {
  message = 'Hello from the Greeting Component!';
}

src/app/greeting/greeting.html

Now open src/app/greeting/greeting.html and replace the existing content with:

<div class="greeting">
  <h2>{{ message }}</h2>
</div>

The {{ message }} syntax is called interpolation and it allows us to display the value of the message property in the template.

src/app/greeting/greeting.css

To make the component look nice, let's add some styles. Open src/app/greeting/greeting.css and add the following:

.greeting {
  padding: 20px;
  border: 2px solid #3498db;
  border-radius: 8px;
  margin: 20px 0;
  background-color: #f8f9fa;
  text-align: center;
}

h2 {
  color: #2c3e50;
  margin: 0;
}

Step 3: Use the Component in Our App

Now let's add our greeting component to the main app. There are some steps needed.

First we need to make the app aware of our new component. To do this we need to add the import to the app.ts file, include it in the root component, and include it in the template.

  1. Import the component in src/app/app.ts:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Greeting } from './greeting/greeting'; // import the Greeting component

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, Greeting], // add Greeting to imports
  templateUrl: './app.html',
  styleUrl: './app.css'
})
export class App {
  protected title = 'angular-module4-demo';
}
  1. Add the component to src/app/app.html:
<main class="main">
  <div class="content">
    <h1>Angular Module 4 Demo</h1>

    <app-greeting></app-greeting>
  </div>
</main>

<router-outlet />

Step 4: Test Your First Component

Your development server should automatically reload. Visit http://localhost:4200 and you should see:

  • The main title "Angular Module 4 Demo"
  • Below it, a blue-bordered box with "Hello from the Greeting Component!"

Your page should look something like this:

Hello from the Greeting Component

🎉 Congratulations! You've just created and used your first Angular component!

Step 5: Adding a Simple Counter Component

Now let's create a second component that demonstrates dynamic data and event handling. We'll build a simple counter that you can increment and reset.

Generate the Counter Component

ng generate component counter
# or shorthand:
ng g c counter

Create the Counter Component

Keep it minimal! Replace the generated counter component with:

src/app/counter/counter.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-counter',
  imports: [],
  templateUrl: './counter.html',
  styleUrl: './counter.css'
})
export class Counter {
  count = 0;

  increment() {
    this.count++;
  }

  reset() {
    this.count = 0;
  }
}

src/app/counter/counter.html

<div class="counter">
  <h3>Counter: {{ count }}</h3>
  <button (click)="increment()">+1</button>
  <button (click)="reset()">Reset</button>
</div>

src/app/counter/counter.css

.counter {
  padding: 20px;
  border: 2px solid #e74c3c;
  border-radius: 8px;
  margin: 20px 0;
  background-color: #fdf2f2;
  text-align: center;
}

button {
  margin: 0 5px;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
}

button:first-of-type {
  background-color: #27ae60;
  color: white;
}

button:last-of-type {
  background-color: #e74c3c;
  color: white;
}

Add Counter to Main App

  1. Import the counter in src/app/app.ts:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Greeting } from './greeting/greeting';
import { Counter } from './counter/counter'; // import Counter

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, Greeting, Counter], // add Counter to imports
  templateUrl: './app.html',
  styleUrl: './app.css'
})
export class App {
  protected title = 'angular-module4-demo';
}
  1. Add counter to template in src/app/app.html:
<main class="main">
  <div class="content">
    <h1>Angular Module 4 Demo</h1>

    <app-greeting></app-greeting>
    <app-counter></app-counter>
  </div>
</main>

<router-outlet />

Test Your Counter

Visit http://localhost:4200 and you should now see:

  • Your greeting component (blue border)
  • Below it, a counter component (red border) with buttons that work!

Try clicking the "+1" and "Reset" buttons to see the dynamic behavior!

Your browser should look like this:

Counting up with Angular

🎯 What You've Learned:

  • Data binding with {{ count }}
  • Event handling with (click)="method()"
  • Component methods that modify data
  • Multiple components in one app

Step 5: Adding a Dynamic Message

Understanding Component Decorator

The @Component decorator is what makes a TypeScript class into an Angular component. Let's understand its key properties:

@Component({
  selector: 'app-greeting',           // HTML tag name: <app-greeting></app-greeting>
  imports: [],                       // Other components/directives this component uses
  templateUrl: './greeting.html',    // External template file
  styleUrl: './greeting.css'         // External style file (Angular 20 uses styleUrl, not styleUrls)

  // Alternative inline options (for small components):
  // template: '<h1>Inline Template</h1>',
  // styles: ['h1 { color: blue; }']
})

Key Decorator Properties Explained

  • selector: The custom HTML tag name for your component
  • Convention: app- prefix followed by component name
  • Used like: <app-greeting></app-greeting>

  • imports: Dependencies your component needs

  • Other components, directives, pipes
  • Angular 20 standalone components manage their own dependencies

  • templateUrl / template: Where to find the HTML template

  • templateUrl: External file (recommended for complex templates)
  • template: Inline HTML (good for simple components)

  • styleUrl / styles: Component-specific CSS

  • styleUrl: External CSS file (Angular 20 syntax)
  • styles: Inline CSS array

🔍 Angular 20 Changes:

  • styleUrl instead of styleUrls (simplified)
  • imports instead of module declarations
  • Standalone components by default (no NgModules required)

Understanding Data Binding

Now that we've created our basic components, let's understand how data binding works with real examples from our demo project.

1. Interpolation - Displaying Data

How it works: Display component properties in the template using {{ }}.

Our greeting component already demonstrates this perfectly:

// In greeting.ts (what we already created)
export class Greeting {
  message = 'Hello from Angular 20!';
}
<!-- In greeting.html (what we already created) -->
<div class="greeting">
  <h2>{{ message }}</h2>
</div>

Key Point: The {{ message }} displays the value of the message property. When the property changes, the display updates automatically.

2. Event Binding - Handling User Actions

How it works: Listen to user events using (event)="method()".

Our counter component demonstrates this with its buttons:

// In counter.ts (what we already created)
export class Counter {
  count = 0;

  increment() {
    this.count++;
  }

  reset() {
    this.count = 0;
  }
}
<!-- In counter.html (what we already created) -->
<div class="counter">
  <h3>Count: {{ count }}</h3>
  <button (click)="increment()">+1</button>
  <button (click)="reset()">Reset</button>
</div>

Key Points:

  • (click)="increment()" listens for click events
  • When clicked, it calls the increment() method
  • The method updates the count property
  • Angular automatically updates the display

3. Property Binding - Setting Element Properties

Now that we have a basic understanding of data binding through our examples, let's explore property binding.

How it works: Set element properties and attributes using [property]="value".

Let's enhance our counter component to demonstrate property binding by making it more dynamic:

// Enhanced counter.ts
export class Counter {
  count = 0;
  maxCount = 10;
  step = 1;

  increment() {
    if (this.count < this.maxCount) {
      this.count += this.step;
    }
  }

  reset() {
    this.count = 0;
  }

  get isAtMax() {
    return this.count >= this.maxCount;
  }

  get isHighCount() {
    return this.count >= 7;
  }
}
<!-- Enhanced counter.html with property binding -->
<div class="counter" [class.high-count]="isHighCount">
  <h3>Count: {{ count }}</h3>
  <button
    (click)="increment()"
    [disabled]="isAtMax"
    [title]="isAtMax ? 'Maximum reached!' : 'Increment by ' + step">
    +{{ step }}
  </button>
  <button
    (click)="reset()"
    [title]="'Reset count to 0'">
    Reset
  </button>
  <p [style.color]="isHighCount ? 'orange' : 'green'">
    {{ isAtMax ? 'Maximum reached!' : count + ' of ' + maxCount }}
  </p>
</div>
/* Enhanced counter.css - add this to your existing styles */
.counter.high-count {
  border-color: #f39c12;
  background-color: #fef9e7;
}

.counter button[disabled] {
  background-color: #95a5a6;
  cursor: not-allowed;
  opacity: 0.6;
}

.counter button[title]:hover::after {
  content: attr(title);
  position: absolute;
  background: #333;
  color: white;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  white-space: nowrap;
  z-index: 1000;
}

Key Points:

  • [disabled]="isAtMax" - Disables button when condition is true
  • [class.high-count]="isHighCount" - Adds CSS class conditionally
  • [title]="..." - Sets tooltip text dynamically
  • [style.color]="..." - Sets inline styles based on conditions
  • Square brackets [] indicate property binding

What this demonstrates:

  • Conditional styling - Border and background change when count is high
  • Dynamic attributes - Button tooltip changes based on state
  • Disabled state - Button becomes unclickable at maximum
  • Conditional classes - CSS class applied when count >= 7

Your app should now look like this:

Screenshot Enhanced Counter With Style

🎯 Exercise: Enhance Your Components

Now that you understand the basics, try enhancing the components we created:

For the Greeting Component:

  1. Add a name property and display "Hello, [name]!"
  2. Add a method to change the greeting message
  3. Add a button that calls the method

For the Counter Component:

  1. Add a decrement button (-1)
  2. Add a step size (increment/decrement by 2, 5, etc.)
  3. Add a maximum limit (can't go above 10)
  4. Change the border color when count is high

Challenge: Create a new component that combines both greeting and counting!

Advanced Topics (Optional)

The sections below cover more advanced component concepts. These are important for larger applications but not essential for getting started. Feel free to explore them when you're comfortable with the basics.

Lifecycle Hooks Interface

import { Component, OnInit, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';

@Component({
  selector: 'app-lifecycle-demo',
  standalone: true,
  template: `
    <h3>Lifecycle Demo</h3>
    <p>Check the console for lifecycle events</p>
  `
})
export class LifecycleDemoComponent implements OnInit, OnChanges, OnDestroy {

  constructor() {
    console.log('Constructor called');
  }

  ngOnChanges(changes: SimpleChanges) {
    console.log('OnChanges called', changes);
  }

  ngOnInit() {
    console.log('OnInit called');
    // Component initialization logic here
  }

  ngDoCheck() {
    console.log('DoCheck called');
  }

  ngAfterContentInit() {
    console.log('AfterContentInit called');
  }

  ngAfterContentChecked() {
    console.log('AfterContentChecked called');
  }

  ngAfterViewInit() {
    console.log('AfterViewInit called');
  }

  ngAfterViewChecked() {
    console.log('AfterViewChecked called');
  }

  ngOnDestroy() {
    console.log('OnDestroy called');
    // Cleanup logic here
  }
}

Lifecycle Hook Order

  1. Constructor - TypeScript class constructor
  2. OnChanges - When input properties change
  3. OnInit - After first OnChanges
  4. DoCheck - Custom change detection
  5. AfterContentInit - After content projection
  6. AfterContentChecked - After every content check
  7. AfterViewInit - After component view initialization
  8. AfterViewChecked - After every view check
  9. OnDestroy - Just before Angular destroys the component

Component Communication

In our demo app, we have multiple components working together. Let's understand how components can communicate.

Simple Component Communication Example

Right now our app.ts file imports and uses both components:

// In app.ts
import { Component } from '@angular/core';
import { Greeting } from './greeting/greeting';
import { Counter } from './counter/counter';

@Component({
  selector: 'app-root',
  imports: [Greeting, Counter],
  templateUrl: './app.html',
  styleUrl: './app.css'
})
export class App {
  title = 'angular-module4-demo';
}

Parent to Child - Input Properties

Let's enhance our demo to show data passing. We could modify our greeting component to accept a name:

// Enhanced greeting.ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-greeting',
  imports: [],
  templateUrl: './greeting.html',
  styleUrl: './greeting.css'
})
export class Greeting {
  @Input() name = 'World';  // Can receive name from parent

  get message() {
    return `Hello, ${this.name}!`;
  }
}

Then the parent can pass data:

<!-- In app.html -->
<app-greeting [name]="'Angular Developer'"></app-greeting>

Child to Parent - Output Properties

We could enhance our counter to notify the parent when the count changes:

// Enhanced counter.ts
import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-counter',
  imports: [],
  templateUrl: './counter.html',
  styleUrl: './counter.css'
})
export class Counter {
  count = 0;
  @Output() countChange = new EventEmitter<number>();

  increment() {
    this.count++;
    this.countChange.emit(this.count);  // Notify parent
  }

  reset() {
    this.count = 0;
    this.countChange.emit(this.count);  // Notify parent
  }
}

And the parent can listen:

<!-- In app.html -->
<app-counter (countChange)="onCountChange($event)"></app-counter>
<p>Counter changed to: {{ currentCount }}</p>
// In app.ts
export class App {
  currentCount = 0;

  onCountChange(newCount: number) {
    this.currentCount = newCount;
    console.log('Count changed to:', newCount);
  }
}

Key Points:

  • @Input() brings data INTO a component from its parent
  • @Output() sends events OUT of a component to its parent
  • This creates a clear data flow: down through inputs, up through outputs

💡 Note: For more advanced component communication patterns like services, observables, and complex parent-child relationships, see Module 9: Component Communication.

Summary

Congratulations! You've learned the fundamentals of Angular components by building real examples:

What We Built:

  • Demo Project: Clean Angular 20 setup
  • Greeting Component: Simple interpolation example
  • Counter Component: Event handling and state management
  • Working App: Multiple components working together

Key Concepts Covered:

  • Component Structure: TypeScript class + HTML template + CSS styles
  • Data Binding: Interpolation ({{}}) and event binding ((click))
  • Component Communication: Input/Output patterns for data flow
  • Angular 20 Features: Standalone components, styleUrl, simplified imports

Practice Suggestions:

Keep experimenting with the demo project:

  • Add more properties to the greeting component
  • Create a new component that combines greeting and counting
  • Try passing data between the components
  • Style the components differently

The best way to learn Angular is by building things - you're off to a great start!


Next Steps

Now that you understand the fundamentals of Angular components, you're ready to dive deeper:

  • Module 5: Angular Modules and Module System - Learn how to organize your application into feature modules
  • Module 7: Introduction to Angular Templates - Master template syntax and make your components more dynamic
  • Module 8: Data Binding - Master all types of data binding in detail
  • Module 9: Component Communication - Learn advanced patterns for component interaction
  • Module 17: Advanced Components - Explore advanced component features like content projection, lifecycle hooks, and architectural patterns

Quick Reference

Component Anatomy (What You Built)

@Component({
  selector: 'app-greeting',        // Custom HTML tag
  imports: [],                     // Dependencies
  templateUrl: './greeting.html',  // Template file
  styleUrl: './greeting.css'       // Styles file
})
export class Greeting {
  message = 'Hello from Angular!'; // Component property

  // Component methods go here
}

What You've Learned

  • Interpolation: {{ message }} - Display component data
  • Event Binding: (click)="increment()" - Handle user interactions
  • Property Binding: [disabled]="isAtMax" - Set element properties dynamically
  • Component Methods: Functions that respond to events and update data
  • Component Properties: Data that gets displayed in the template

Keep practicing with the demo project and experiment with these concepts!