Angular: Framework Basics
Angular is a comprehensive framework for building enterprise applications. This article covers Angular fundamentals.
Introduction
Angular provides:
- Component-based architecture
- Dependency injection
- TypeScript support
- RxJS integration
- Powerful CLI tools
Understanding Angular helps you:
- Build enterprise applications
- Create reusable components
- Manage application state
- Handle asynchronous operations
- Scale large applications
Angular Setup
Installation and Project Setup
# โ
Good: Install Angular CLI
npm install -g @angular/cli
# โ
Good: Create new Angular project
ng new my-app
cd my-app
ng serve
# โ
Good: Generate components
ng generate component components/user-card
ng g c components/user-list
# โ
Good: Generate services
ng generate service services/user
ng g s services/auth
Project Structure
my-app/
โโโ src/
โ โโโ app/
โ โ โโโ components/
โ โ โโโ services/
โ โ โโโ models/
โ โ โโโ app.component.ts
โ โ โโโ app.component.html
โ โ โโโ app.component.css
โ โ โโโ app.module.ts
โ โโโ assets/
โ โโโ main.ts
โ โโโ index.html
โโโ angular.json
โโโ tsconfig.json
โโโ package.json
Angular Components
Basic Component
// user-card.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core'
@Component({
selector: 'app-user-card',
templateUrl: './user-card.component.html',
styleUrls: ['./user-card.component.css']
})
export class UserCardComponent {
@Input() user: any
@Output() edit = new EventEmitter<number>()
onEdit() {
this.edit.emit(this.user.id)
}
}
// user-card.component.html
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>Email: {{ user.email }}</p>
<button (click)="onEdit()">Edit</button>
</div>
// user-card.component.css
.user-card {
border: 1px solid #ccc;
padding: 1rem;
border-radius: 4px;
}
Component Lifecycle
// โ
Good: Component with lifecycle hooks
import {
Component,
OnInit,
OnDestroy,
OnChanges,
SimpleChanges
} from '@angular/core'
@Component({
selector: 'app-user-profile',
templateUrl: './user-profile.component.html'
})
export class UserProfileComponent implements OnInit, OnDestroy, OnChanges {
user: any
ngOnInit() {
console.log('Component initialized')
this.loadUser()
}
ngOnChanges(changes: SimpleChanges) {
console.log('Input properties changed', changes)
}
ngOnDestroy() {
console.log('Component destroyed')
}
loadUser() {
// Load user data
}
}
Two-Way Binding
// form.component.ts
import { Component } from '@angular/core'
@Component({
selector: 'app-form',
templateUrl: './form.component.html'
})
export class FormComponent {
formData = {
email: '',
password: '',
rememberMe: false
}
onSubmit() {
console.log('Form submitted:', this.formData)
}
}
// form.component.html
<form (ngSubmit)="onSubmit()">
<input
[(ngModel)]="formData.email"
name="email"
type="email"
placeholder="Email"
/>
<input
[(ngModel)]="formData.password"
name="password"
type="password"
placeholder="Password"
/>
<label>
<input
[(ngModel)]="formData.rememberMe"
name="rememberMe"
type="checkbox"
/>
Remember me
</label>
<button type="submit">Login</button>
</form>
Angular Services
Basic Service
// user.service.ts
import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { Observable } from 'rxjs'
@Injectable({
providedIn: 'root'
})
export class UserService {
private apiUrl = '/api/users'
constructor(private http: HttpClient) {}
getUsers(): Observable<any[]> {
return this.http.get<any[]>(this.apiUrl)
}
getUser(id: number): Observable<any> {
return this.http.get<any>(`${this.apiUrl}/${id}`)
}
createUser(user: any): Observable<any> {
return this.http.post<any>(this.apiUrl, user)
}
updateUser(id: number, user: any): Observable<any> {
return this.http.put<any>(`${this.apiUrl}/${id}`, user)
}
deleteUser(id: number): Observable<any> {
return this.http.delete<any>(`${this.apiUrl}/${id}`)
}
}
// user-list.component.ts
import { Component, OnInit } from '@angular/core'
import { UserService } from '../services/user.service'
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html'
})
export class UserListComponent implements OnInit {
users: any[] = []
loading = true
error: string | null = null
constructor(private userService: UserService) {}
ngOnInit() {
this.loadUsers()
}
loadUsers() {
this.userService.getUsers().subscribe({
next: (data) => {
this.users = data
this.loading = false
},
error: (err) => {
this.error = err.message
this.loading = false
}
})
}
}
Dependency Injection
// โ
Good: Dependency injection
import { Injectable } from '@angular/core'
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private http: HttpClient) {}
login(email: string, password: string) {
return this.http.post('/api/login', { email, password })
}
}
// Component using injected service
import { Component } from '@angular/core'
@Component({
selector: 'app-login',
templateUrl: './login.component.html'
})
export class LoginComponent {
constructor(private authService: AuthService) {}
onLogin(email: string, password: string) {
this.authService.login(email, password).subscribe({
next: (response) => {
console.log('Login successful')
},
error: (error) => {
console.error('Login failed')
}
})
}
}
Angular Routing
Basic Routing
// app-routing.module.ts
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
import { HomeComponent } from './pages/home.component'
import { AboutComponent } from './pages/about.component'
import { UserProfileComponent } from './pages/user-profile.component'
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'users/:id', component: UserProfileComponent },
{ path: '**', redirectTo: '' }
]
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
// app.component.html
<nav>
<a routerLink="/">Home</a>
<a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet>
Route Parameters
// user-profile.component.ts
import { Component, OnInit } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { UserService } from '../services/user.service'
@Component({
selector: 'app-user-profile',
templateUrl: './user-profile.component.html'
})
export class UserProfileComponent implements OnInit {
user: any
constructor(
private route: ActivatedRoute,
private userService: UserService
) {}
ngOnInit() {
this.route.params.subscribe((params) => {
const userId = params['id']
this.userService.getUser(userId).subscribe((data) => {
this.user = data
})
})
}
}
RxJS and Observables
Working with Observables
// โ
Good: Observable subscription
import { Component, OnInit, OnDestroy } from '@angular/core'
import { Subject } from 'rxjs'
import { takeUntil } from 'rxjs/operators'
@Component({
selector: 'app-data-list',
templateUrl: './data-list.component.html'
})
export class DataListComponent implements OnInit, OnDestroy {
data: any[] = []
private destroy$ = new Subject<void>()
constructor(private dataService: DataService) {}
ngOnInit() {
this.dataService.getData()
.pipe(takeUntil(this.destroy$))
.subscribe((data) => {
this.data = data
})
}
ngOnDestroy() {
this.destroy$.next()
this.destroy$.complete()
}
}
// โ
Good: Async pipe
// component.ts
export class UserListComponent {
users$ = this.userService.getUsers()
constructor(private userService: UserService) {}
}
// component.html
<div *ngFor="let user of users$ | async">
{{ user.name }}
</div>
// โ
Good: Combining observables
import { combineLatest } from 'rxjs'
export class DashboardComponent implements OnInit {
constructor(
private userService: UserService,
private postService: PostService
) {}
ngOnInit() {
combineLatest([
this.userService.getUsers(),
this.postService.getPosts()
]).subscribe(([users, posts]) => {
console.log('Users:', users)
console.log('Posts:', posts)
})
}
}
Angular Forms
Reactive Forms
// form.component.ts
import { Component, OnInit } from '@angular/core'
import { FormBuilder, FormGroup, Validators } from '@angular/forms'
@Component({
selector: 'app-form',
templateUrl: './form.component.html'
})
export class FormComponent implements OnInit {
form: FormGroup
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
rememberMe: [false]
})
}
ngOnInit() {}
onSubmit() {
if (this.form.valid) {
console.log('Form submitted:', this.form.value)
}
}
}
// form.component.html
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input
formControlName="email"
type="email"
placeholder="Email"
/>
<div *ngIf="form.get('email')?.hasError('required')">
Email is required
</div>
<input
formControlName="password"
type="password"
placeholder="Password"
/>
<div *ngIf="form.get('password')?.hasError('minlength')">
Password must be at least 8 characters
</div>
<label>
<input formControlName="rememberMe" type="checkbox" />
Remember me
</label>
<button type="submit" [disabled]="!form.valid">
Login
</button>
</form>
Best Practices
-
Use services for data:
// โ Good: Service for data constructor(private userService: UserService) {} // โ Bad: Direct HTTP calls constructor(private http: HttpClient) {} -
Use async pipe:
<!-- โ Good: Async pipe --> <div *ngFor="let user of users$ | async"> {{ user.name }} </div> <!-- โ Bad: Manual subscription --> <div *ngFor="let user of users"> {{ user.name }} </div> -
Unsubscribe from observables:
// โ Good: Unsubscribe private destroy$ = new Subject<void>() ngOnInit() { this.service.data$ .pipe(takeUntil(this.destroy$)) .subscribe(...) } ngOnDestroy() { this.destroy$.next() }
Summary
Angular is powerful. Key takeaways:
- Use components for UI
- Use services for data
- Use dependency injection
- Implement routing
- Use RxJS observables
- Use reactive forms
- Follow Angular best practices
- Build scalable applications
Related Resources
Next Steps
- Learn about Backend Development
- Explore Express.js
- Study Routing and Middleware
- Practice Angular
- Build Angular applications
Comments