Skip to main content

Angular Component Basics

Introduction

Components are the fundamental building blocks of Angular applications. They define views, which are sets of screen elements that Angular can choose among and modify according to your program logic and data. Every Angular application has at least one component, known as the root component, which connects a component hierarchy with the page DOM.

In simple terms, a component is a TypeScript class with a template that defines a view and a decorator that adds metadata, including a selector that defines how the component is used in HTML.

In this lesson, we'll explore the basics of Angular components, learn how to create them, understand their structure, and see how they work together to build an application.

What Is an Angular Component?

An Angular component consists of three main parts:

  1. A TypeScript class that defines the behavior
  2. An HTML template that determines the UI
  3. CSS styles (optional) for styling the component

Here's a visual representation of component architecture:

Component
├── Class (TypeScript)
│ ├── Properties
│ └── Methods
├── Template (HTML)
└── Styles (CSS)

Creating Your First Component

Let's create a simple "Hello World" component to understand the basics.

Step 1: Generate a Component

The easiest way to create a component is to use the Angular CLI:

bash
ng generate component hello-world
# or the shorter version
ng g c hello-world

This command creates:

  • A directory called hello-world/
  • A component file hello-world.component.ts
  • A template file hello-world.component.html
  • A CSS file hello-world.component.css
  • A testing specification file hello-world.component.spec.ts

Step 2: Understanding the Component File

Let's look at the generated component TypeScript file:

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

@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent {
// Component logic goes here
}

Let's break down what's happening:

  • import { Component } from '@angular/core'; - Imports the Component decorator
  • @Component({...}) - Decorator that marks the class as an Angular component
  • selector: 'app-hello-world' - Defines how to use this component in HTML (<app-hello-world></app-hello-world>)
  • templateUrl - Points to the HTML file that defines the component's view
  • styleUrls - Array of CSS files for styling the component
  • export class HelloWorldComponent {} - The component class where our logic resides

Step 3: Adding Content to the Component

Let's modify our component to display a message and provide some interactivity:

  1. Update the TypeScript file:
typescript
import { Component } from '@angular/core';

@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent {
message: string = 'Hello, Angular World!';
clickCount: number = 0;

incrementCount() {
this.clickCount++;
if (this.clickCount > 10) {
this.message = "You're clicking a lot!";
}
}
}
  1. Update the HTML template file:
html
<div class="hello-container">
<h2>{{ message }}</h2>
<p>Button has been clicked: {{ clickCount }} times</p>
<button (click)="incrementCount()">Click me!</button>
</div>
  1. Add some simple styles:
css
.hello-container {
border: 2px solid #3f51b5;
padding: 20px;
border-radius: 8px;
max-width: 400px;
margin: 20px auto;
text-align: center;
}

button {
background-color: #3f51b5;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
}

button:hover {
background-color: #303f9f;
}

Understanding Data Binding

Our example demonstrates three important concepts in Angular components:

1. Interpolation ({{ }})

In our HTML template, we used {{ message }} and {{ clickCount }}. This is called interpolation, which allows us to display component property values in the view.

2. Event Binding ((event))

The (click)="incrementCount()" syntax is event binding, which lets you listen for DOM events like clicks and execute a component method when they happen.

3. Data Flow

When a user clicks the button:

  1. Angular detects the click event
  2. The incrementCount() method runs
  3. The clickCount property increases
  4. Angular updates the view automatically

Component Communication

Components often need to communicate with each other. Angular provides several mechanisms for this:

Input Properties

Use @Input() decorators to pass data from parent to child components:

typescript
// Parent component template
<app-child-component [dataItem]="parentData"></app-child-component>

// Child component
import { Component, Input } from '@angular/core';

@Component({
selector: 'app-child-component',
template: '<p>Received: {{ dataItem }}</p>'
})
export class ChildComponent {
@Input() dataItem: string;
}

Output Properties

Use @Output() with EventEmitter to send data from child to parent:

typescript
// Child component
import { Component, Output, EventEmitter } from '@angular/core';

@Component({
selector: 'app-child-component',
template: '<button (click)="sendMessage()">Send to Parent</button>'
})
export class ChildComponent {
@Output() messageEvent = new EventEmitter<string>();

sendMessage() {
this.messageEvent.emit('Hello from child!');
}
}

// Parent component template
<app-child-component (messageEvent)="receiveMessage($event)"></app-child-component>

// Parent component class
receiveMessage(msg: string) {
console.log(msg); // "Hello from child!"
}

Real-World Example: Task Item Component

Let's build a more practical component for a todo application. This component represents a single task item:

typescript
// task-item.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
selector: 'app-task-item',
templateUrl: './task-item.component.html',
styleUrls: ['./task-item.component.css']
})
export class TaskItemComponent {
@Input() task: { id: number; title: string; completed: boolean };
@Output() toggleComplete = new EventEmitter<number>();
@Output() deleteTask = new EventEmitter<number>();

onToggle() {
this.toggleComplete.emit(this.task.id);
}

onDelete() {
this.deleteTask.emit(this.task.id);
}
}
html
<!-- task-item.component.html -->
<div class="task-item" [class.completed]="task.completed">
<input
type="checkbox"
[checked]="task.completed"
(change)="onToggle()">
<span class="task-title">{{ task.title }}</span>
<button class="delete-btn" (click)="onDelete()">Delete</button>
</div>
css
/* task-item.component.css */
.task-item {
display: flex;
align-items: center;
padding: 12px;
border-bottom: 1px solid #eee;
}

.completed .task-title {
text-decoration: line-through;
color: #888;
}

.task-title {
flex-grow: 1;
margin: 0 15px;
}

.delete-btn {
background: #ff4081;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
}

And a parent component that uses our task-item component:

typescript
// task-list.component.ts
import { Component } from '@angular/core';

@Component({
selector: 'app-task-list',
template: `
<div class="task-list">
<h2>My Tasks</h2>
<app-task-item
*ngFor="let task of tasks"
[task]="task"
(toggleComplete)="toggleTaskStatus($event)"
(deleteTask)="removeTask($event)">
</app-task-item>
</div>
`
})
export class TaskListComponent {
tasks = [
{ id: 1, title: 'Learn Angular Components', completed: false },
{ id: 2, title: 'Create first application', completed: false },
{ id: 3, title: 'Understand data binding', completed: true }
];

toggleTaskStatus(taskId: number) {
const task = this.tasks.find(t => t.id === taskId);
if (task) {
task.completed = !task.completed;
}
}

removeTask(taskId: number) {
this.tasks = this.tasks.filter(t => t.id !== taskId);
}
}

This example demonstrates:

  • Component reusability (each task uses the same component)
  • Input/Output properties for component communication
  • Event handling
  • Class binding ([class.completed])
  • Handling collections with *ngFor

Component Lifecycle

Angular components go through a lifecycle from creation to destruction. Angular provides hooks to tap into these lifecycle events:

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

@Component({
selector: 'app-lifecycle-demo',
template: '<p>{{ message }}</p>'
})
export class LifecycleDemoComponent implements OnInit, OnDestroy {
message: string;

constructor() {
// Called first when the component is created
this.message = 'Component created';
}

ngOnInit() {
// Called after the constructor
// Best place to initialize your component
this.message = 'Component initialized';
console.log('Component initialized');
}

ngOnDestroy() {
// Called just before Angular destroys the component
// Perfect for cleanup logic
console.log('Component is being destroyed');
}
}

The most commonly used lifecycle hooks are:

  • ngOnInit - Initialize the component
  • ngOnChanges - Respond when Angular sets data-bound input properties
  • ngOnDestroy - Cleanup just before Angular destroys the component

Summary

In this lesson, we've covered the basics of Angular components:

  • Components are the fundamental building blocks of Angular applications
  • A component has a TypeScript class, HTML template, and optional CSS styles
  • The @Component decorator configures the component
  • Data binding connects component class properties to the template
  • Components can communicate via Input and Output properties
  • The Angular component lifecycle provides hooks for initialization and cleanup

By understanding these fundamentals, you're on your way to building robust Angular applications with reusable, maintainable components.

Exercises

  1. Create a profile card component that displays a user's name, profile picture, and bio.
  2. Build a simple counter component with increment and decrement buttons.
  3. Create a parent-child component relationship where data is passed down and events are emitted up.
  4. Build a component that uses multiple lifecycle hooks and logs when each is executed.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)