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:
- A TypeScript class that defines the behavior
- An HTML template that determines the UI
- 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:
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:
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 componentselector: '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 viewstyleUrls
- Array of CSS files for styling the componentexport 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:
- Update the TypeScript file:
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!";
}
}
}
- Update the HTML template file:
<div class="hello-container">
<h2>{{ message }}</h2>
<p>Button has been clicked: {{ clickCount }} times</p>
<button (click)="incrementCount()">Click me!</button>
</div>
- Add some simple styles:
.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:
- Angular detects the click event
- The
incrementCount()
method runs - The
clickCount
property increases - 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:
// 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:
// 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:
// 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);
}
}
<!-- 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>
/* 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:
// 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:
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 componentngOnChanges
- Respond when Angular sets data-bound input propertiesngOnDestroy
- 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
- Create a profile card component that displays a user's name, profile picture, and bio.
- Build a simple counter component with increment and decrement buttons.
- Create a parent-child component relationship where data is passed down and events are emitted up.
- 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! :)