Components and Templates
Level: Beginner
- Component structure and decorators
- Template syntax (interpolation, binding)
- Property binding and event binding
- Two-way binding with ngModel
- Component communication (Input/Output)
- Component lifecycle
Why This Matters
Components = building blocks of Angular applications. Understanding components and templates is foundation for everything else. Every UI element = component.
Component Structure
import { Component } from '@angular/core';
@Component({
selector: 'app-student-card',
template: `
<div>
<h2>{{ student.name }}</h2>
<p>{{ student.rollNumber }}</p>
</div>
`,
styles: [`
div { padding: 10px; border: 1px solid #ccc; }
`]
})
export class StudentCardComponent {
student = {
name: 'Ravi Kumar',
rollNumber: '101'
};
}
@Component decorator configures component.
selector = HTML tag to use.
template = inline HTML (or templateUrl for external file).
styles = inline CSS (or styleUrls for external files).
Template Syntax
Interpolation
<!-- app.component.html -->
<h1>{{ title }}</h1>
<p>{{ student.name }}</p>
<p>{{ getStudentClass() }}</p>
{{ }} = interpolation. Displays component property value.
Property Binding
<img [src]="imageUrl">
<button [disabled]="isSubmitting">Submit</button>
<div [class.active]="isActive">Active</div>
<div [style.color]="color">Text</div>
[property]="value" = bind component property to element property.
Event Binding
<button (click)="onSubmit()">Submit</button>
<input (keyup)="onKeyUp($event)">
<form (submit)="handleSubmit()">
(event)="handler()" = listen to element events.
Two-Way Binding
<input [(ngModel)]="studentName">
<!-- studentName updates when input changes, and vice versa -->
[(ngModel)] = two-way binding. Property and event combined.
Requires FormsModule import.
SMS Component Example
// student-form.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-student-form',
templateUrl: './student-form.component.html',
styleUrls: ['./student-form.component.css']
})
export class StudentFormComponent {
student = {
name: '',
email: '',
className: '10'
};
classes = ['9', '10', '11', '12'];
submitted = false;
onSubmit() {
if (this.student.name && this.student.email) {
console.log('Student:', this.student);
this.submitted = true;
this.resetForm();
}
}
resetForm() {
this.student = { name: '', email: '', className: '10' };
}
}
<!-- student-form.component.html -->
<form (ngSubmit)="onSubmit()">
<div>
<label>Name:</label>
<input
type="text"
[(ngModel)]="student.name"
name="name"
required
>
</div>
<div>
<label>Email:</label>
<input
type="email"
[(ngModel)]="student.email"
name="email"
required
>
</div>
<div>
<label>Class:</label>
<select [(ngModel)]="student.className" name="className">
<option *ngFor="let cls of classes" [value]="cls">
{{ cls }}
</option>
</select>
</div>
<button type="submit" [disabled]="!student.name || !student.email">
Register
</button>
</form>
<div *ngIf="submitted">
Student {{ student.name }} registered!
</div>
Component Input/Output
Parent to Child (Input)
// parent.component.ts
export class StudentListComponent {
students = [
{ id: 1, name: 'Ravi', className: '10' },
{ id: 2, name: 'Priya', className: '10' }
];
}
<!-- parent.component.html -->
<app-student-card *ngFor="let student of students" [student]="student">
</app-student-card>
// student-card.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-student-card',
templateUrl: './student-card.component.html'
})
export class StudentCardComponent {
@Input() student: any;
}
@Input() = receive data from parent.
[student]="student" = pass data to child.
Child to Parent (Output)
// student-card.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-student-card',
templateUrl: './student-card.component.html'
})
export class StudentCardComponent {
@Input() student: any;
@Output() delete = new EventEmitter<number>();
onDelete() {
this.delete.emit(this.student.id);
}
}
<!-- student-card.component.html -->
<div>
<h3>{{ student.name }}</h3>
<button (click)="onDelete()">Delete</button>
</div>
<!-- parent.component.html -->
<app-student-card
*ngFor="let student of students"
[student]="student"
(delete)="onDeleteStudent($event)"
>
</app-student-card>
// parent.component.ts
onDeleteStudent(id: number) {
this.students = this.students.filter(s => s.id !== id);
}
@Output() = send event to parent.
(delete)="handler($event)" = listen to child event.
Component Lifecycle
export class MyComponent implements OnInit, OnDestroy {
ngOnInit() {
// Called after component initialized
// Load data here
}
ngOnDestroy() {
// Called before component destroyed
// Cleanup here (unsubscribe, etc)
}
}
Common hooks:
ngOnInit— Initialize componentngOnDestroy— CleanupngOnChanges— Input changedngAfterViewInit— View rendered
Key Takeaways
- Component = class + template + styles
- Interpolation
{{ }}displays values - Property binding
[prop]="value"binds to element - Event binding
(event)="handler()"listens to events - Two-way binding
[(ngModel)]="property"both ways @Input()= receive from parent@Output()= send to parent- Lifecycle hooks for initialization/cleanup
Angular components are like MVC views. Template = View, Component class = Controller/ViewModel. Familiar structure if you know ASP.NET MVC or Razor Pages.
- Forgetting name attribute in form — ngModel won't work
- *Using object without trackBy in ngFor — Performance issue
- Forgetting to import FormsModule — ngModel not recognized
- Modifying Input directly — Child shouldn't modify @Input
- Not unsubscribing from Observables — Memory leaks
Use ChatGPT, Claude, or Copilot to go deeper on Angular Components. Try these prompts:
"What's difference between property and event binding?""How do parent and child components communicate?""Why use @Input instead of passing in constructor?""Quiz me on component templates"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.