Angular Components Explained — Complete Guide to Building Reusable UI
In this tutorial, you will learn about Angular Components Explained. We cover key concepts, practical examples, and best practices to help you master this topic.
Angular components are the building blocks of any Angular application, encapsulating template logic, styles, and behavior into reusable, testable units.
What You'll Learn
- What Angular components are and why they matter
- How to create components with the Angular CLI
- How selectors, templates, and styles work together
- How to pass data with @Input and emit events with @Output
- How to compose complex UIs from small components
Why It Matters
Components let you break a complex UI into small, independent pieces that are easier to build, test, and maintain. Every Angular app is a tree of components, and mastering them unlocks the full power of the framework.
Real-World Use
Think of a dashboard like the Durga Antivirus Pro control panel. The threat graph is one component, the scan button is another, and the file list is a third. Each is built and tested in isolation, then composed into a full page.
flowchart TD
A[App Root] --> B[Header]
A --> C[Sidebar]
A --> D[Main Content]
D --> E[Threat Graph]
D --> F[Scan Controls]
D --> G[File List]
style A fill:#f97316,color:#fff
What Is an Angular Component?
A component controls a patch of screen called a view. It consists of three things:
- A TypeScript class that handles data and logic
- An HTML template that declares what renders
- CSS styles scoped to that component
Think of a component like a stamp. The class is the stamp's handle (you grip it), the template is the rubber face that leaves an impression, and the styles are the ink color.
The @Component Decorator
Every component starts with the @Component decorator, which tells Angular the metadata it needs:
import { Component } from "@angular/core";
@Component({
selector: "app-greeting",
standalone: true,
template: `
<h1>Hello, {{ name }}!</h1>
<p>Welcome to Angular components.</p>
`,
styles: [`
h1 { color: #f97316; font-family: sans-serif; }
p { font-size: 1.1rem; }
`]
})
export class GreetingComponent {
name = "Angular Developer";
}
Expected output: A page with "Hello, Angular Developer!" in orange and a subtitle below.
The selector is the custom HTML tag you use to place this component. When Angular sees <app-greeting></app-greeting> in another template, it renders this component's view.
Using a Component
Once created, you use a component by adding its tag to another template:
import { Component } from "@angular/core";
import { GreetingComponent } from "./greeting/greeting.component";
@Component({
selector: "app-root",
standalone: true,
imports: [GreetingComponent],
template: `
<app-greeting></app-greeting>
<p>This is the root component.</p>
`
})
export class AppComponent {}
Expected output: The greeting component renders above the root component's text.
The imports array tells Angular that AppComponent depends on GreetingComponent. Without it, Angular would not know what <app-greeting> means.
Passing Data with @Input
Components often need data from their parent. Use the @Input decorator to define a property that accepts incoming values:
import { Component, Input } from "@angular/core";
@Component({
selector: "app-user-card",
standalone: true,
template: `
<div class="card">
<h3>{{ name }}</h3>
<p>{{ email }}</p>
<span class="badge" [style.background]="active ? "green" : "gray"">
{{ active ? "Active" : "Inactive" }}
</span>
</div>
`,
styles: [`
.card { border: 1px solid #ddd; padding: 16px; border-radius: 8px; }
.badge { color: white; padding: 4px 8px; border-radius: 4px; font-size: 0.8rem; }
`]
})
export class UserCardComponent {
@Input() name = "";
@Input() email = "";
@Input() active = false;
}
The parent uses property binding to pass values:
<app-user-card [name]="user.name" [email]="user.email" [active]="user.active"></app-user-card>
Expected output: A card displaying the user's name, email, and a colored active/inactive badge.
The brackets [] around the property tell Angular this is a property binding, not a static attribute. The value inside quotes is a JavaScript expression evaluated against the parent component.
Emitting Events with @Output
Components notify their parent about events using @Output with EventEmitter:
import { Component, Input, Output, EventEmitter } from "@angular/core";
@Component({
selector: "app-counter",
standalone: true,
template: `
<div>
<p>Count: {{ count }}</p>
<button (click)="increment()">+</button>
<button (click)="reset.emit()">Reset</button>
</div>
`
})
export class CounterComponent {
@Input() count = 0;
@Output() countChange = new EventEmitter<number>();
@Output() reset = new EventEmitter<void>();
increment() {
this.countChange.emit(this.count + 1);
}
}
The parent listens to events with parentheses:
<app-counter [count]="counterValue" (countChange)="counterValue = $event" (reset)="counterValue = 0"></app-counter>
Expected output: A counter that increments when the plus button is clicked and resets when the reset button is clicked.
The $event variable holds whatever was emitted. For countChange, it is a number. Angular's event binding syntax (eventName) matches the @Output() property name.
Common Mistakes
Forgetting to import a component — If Angular throws an error like "app-greeting is not a known element", you forgot to add the component to the
importsarray of the parent.Using @Input without brackets in the template — Writing
<app-card name="user.name">passes the literal string "user.name", not the value of the variable. Use[name]="user.name"instead.Mutating @Input properties directly — Input properties should be treated as read-only. If you need to modify the value, emit an event and let the parent handle the change.
Selectors that clash with native HTML — Always prefix your selectors (e.g.,
app-orcmp-). A selector namedcardcould conflict with future HTML elements or third-party components.Putting logic in the template — Templates should only contain expressions, not statements. Fetching data, transforming values, or calling methods that modify state should happen in the class.
Practice Questions
What three parts make up an Angular component? The TypeScript class (logic), the HTML template (view), and the CSS styles (presentation).
What does
standalone: truemean in a component? The component manages its own dependencies via theimportsarray instead of requiring a parent NgModule.How do you pass a string to a component's @Input? Use property binding:
<app-child [inputName]="'string value'"></app-child>or<app-child inputName="string value"></app-child>for static strings.What is the difference between
@Inputand@Output?@Inputlets a parent pass data into the component.@Outputlets the component emit events back to the parent.What happens if you forget the
importsarray? Angular throws a template parse error because it does not recognize the child component's selector.
Challenge
Build a ProductCardComponent with @Input() for name, price, and inStock (boolean). Use @Output() to emit an addToCart event. The card should show a gray "Out of Stock" badge when inStock is false and a green "Add to Cart" button when true. Compose three product cards in the root component.
FAQ
Mini Project
Build a TaskBoardComponent that displays a list of tasks. Create a TaskCardComponent with @Input() for title, description, priority (high/medium/low), and completed (boolean). Use @Output() to emit a toggleComplete event. The board should show three columns for each priority level. Style cards with colored borders based on priority.
// task-card.component.ts
import { Component, Input, Output, EventEmitter } from "@angular/core";
@Component({
selector: "app-task-card",
standalone: true,
template: `
<div class="task-card" [style.border-left]="borderColor">
<h4>{{ title }}</h4>
<p>{{ description }}</p>
<button (click)="toggle.emit()">
{{ completed ? "Undo" : "Complete" }}
</button>
</div>
`,
styles: [`
.task-card { padding: 12px; margin: 8px 0; border: 1px solid #ddd; border-radius: 6px; }
`]
})
export class TaskCardComponent {
@Input() title = "";
@Input() description = "";
@Input() priority: "high" | "medium" | "low" = "medium";
@Input() completed = false;
@Output() toggle = new EventEmitter<void>();
get borderColor() {
return this.priority === "high" ? "4px solid #ef4444"
: this.priority === "medium" ? "4px solid #f97316"
: "4px solid #22c55e";
}
}
What's Next
Now that you understand components, learn how templates and data binding work:
Angular Templates, Angular Directives, Angular Services
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro