Table of Contents
- What is Angular?
- Prerequisites
- Setting Up Your Development Environment
- Core Concepts
- Routing & Navigation
- Forms: Template-Driven vs. Reactive
- HTTP Client: Fetching & Sending Data
- State Management
- Testing Angular Apps
- Deployment
- Best Practices
- Advanced Topics
- Conclusion
- References
What is Angular?
Angular is a platform and framework for building client-side applications using HTML, CSS, and TypeScript (a superset of JavaScript). Unlike libraries like React or Vue, Angular is a full-featured framework that provides out-of-the-box tools for routing, forms, state management, and more.
Key Features:
- TypeScript: Strong typing for better code quality and tooling.
- Component-Based Architecture: Break apps into reusable, modular components.
- Dependency Injection: Simplifies testing and modularity.
- Two-Way Data Binding: Syncs data between the model and view seamlessly.
- Routing: Built-in router for SPAs with lazy loading support.
- Reactive Programming: Uses RxJS for handling asynchronous operations (e.g., API calls).
Prerequisites
Before diving into Angular, ensure you have:
- Basic knowledge of HTML, CSS, and JavaScript (ES6+ features like arrow functions, classes, and promises).
- Familiarity with TypeScript (optional but recommended, as Angular is written in TypeScript).
- Node.js (v14.0.0 or later) and npm (v6.0.0 or later) installed.
Setting Up Your Development Environment
Step 1: Install Node.js and npm
Download Node.js from nodejs.org. npm is included with Node.js. Verify installation:
node -v # Should return v14+
npm -v # Should return v6+
Step 2: Install Angular CLI
The Angular CLI (Command Line Interface) simplifies project setup, development, and deployment. Install it globally:
npm install -g @angular/cli
Verify installation:
ng version # Should display Angular CLI version (e.g., 16.0.0)
Step 3: Create Your First Angular App
Run the following command to create a new project:
ng new my-angular-app
During setup, you’ll be prompted to:
- Add Angular routing? (Select
Yesfor SPAs.) - Choose a stylesheet format (CSS, SCSS, Sass, Less, or Stylus).
Navigate to the project folder and start the development server:
cd my-angular-app
ng serve --open # --open launches the app in your default browser (http://localhost:4200)
Core Concepts
Components
Components are the building blocks of Angular apps. Each component controls a part of the UI with a template, class, and styles.
Anatomy of a Component:
- Class: Contains the component’s logic (properties, methods) and is decorated with
@Component. - Template: HTML that defines the component’s view.
- Styles: CSS/SCSS that style the component (encapsulated by default).
Example: app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root', // HTML tag to use the component (e.g., <app-root></app-root>)
templateUrl: './app.component.html', // Path to the template
styleUrls: ['./app.component.css'] // Path to styles
})
export class AppComponent {
title = 'My Angular App'; // Property bound to the template
}
Component Lifecycle Hooks
Components have lifecycle events (e.g., initialization, destruction). Common hooks:
ngOnInit(): Runs after the component is initialized.ngOnDestroy(): Runs before the component is destroyed (cleanup subscriptions here).
Modules
Angular apps are organized into NgModules, which group related components, directives, pipes, and services. The root module (AppModule) bootstraps the app.
Example: app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent], // Components/directives/pipes belonging to this module
imports: [BrowserModule, AppRoutingModule], // Dependencies (other modules)
providers: [], // Services available to the module
bootstrap: [AppComponent] // Root component to bootstrap
})
export class AppModule { }
Templates & Data Binding
Templates define the UI and use data binding to connect the component class to the view.
Types of Binding:
-
Interpolation: Display component data in the template using
{{ }}.<h1>{{ title }}</h1> <!-- Displays "My Angular App" --> -
Property Binding: Set HTML element properties using
[property].<img [src]="imageUrl"> <!-- Binds the component's `imageUrl` to the img's src --> -
Event Binding: Respond to user events using
(event).<button (click)="onClick()">Click Me</button>In the component class:
onClick() { alert('Button clicked!'); } -
Two-Way Binding: Sync data between the model and view using
[(ngModel)](requiresFormsModule).<input [(ngModel)]="name"> <!-- Updates `name` when the input changes, and vice versa -->
Directives
Directives modify the DOM by adding/removing elements or changing behavior.
1. Structural Directives
Alter the DOM layout by adding/removing elements. Prefix with *.
*ngIf: Conditionally render content.<p *ngIf="isLoggedIn">Welcome, User!</p>*ngFor: Loop over a list.<ul> <li *ngFor="let item of items">{{ item.name }}</li> </ul>
2. Attribute Directives
Change the appearance/behavior of an element.
ngClass: Dynamically add/remove CSS classes.<div [ngClass]="{ active: isActive, disabled: isDisabled }"></div>ngStyle: Dynamically set styles.<p [ngStyle]="{ color: textColor, fontSize: '16px' }"></p>
Services & Dependency Injection
Services are reusable classes for shared logic (e.g., API calls, data storage). Angular’s Dependency Injection (DI) system injects services into components, making code modular and testable.
Step 1: Create a Service
Generate a service with Angular CLI:
ng generate service user
Step 2: Define the Service
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // Makes the service a singleton (available app-wide)
})
export class UserService {
getUsers() {
return ['Alice', 'Bob', 'Charlie'];
}
}
Step 3: Inject the Service into a Component
import { Component } from '@angular/core';
import { UserService } from './user.service';
@Component({ ... })
export class UserComponent {
users: string[];
constructor(private userService: UserService) { // Inject the service
this.users = this.userService.getUsers();
}
}
Routing & Navigation
Angular’s RouterModule enables navigation between views in SPAs.
Step 1: Define Routes
In app-routing.module.ts:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
const routes: Routes = [
{ path: '', component: HomeComponent }, // Default route
{ path: 'about', component: AboutComponent }, // /about route
{ path: '**', redirectTo: '' } // 404: redirect to home
];
@NgModule({
imports: [RouterModule.forRoot(routes)], // forRoot() for root routing
exports: [RouterModule]
})
export class AppRoutingModule { }
Step 2: Add Router Outlet
In app.component.html, add <router-outlet> to display routed components:
<nav>
<a routerLink="/">Home</a> | <a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet> <!-- Renders the active component here -->
Dynamic Routes
Pass parameters (e.g., user IDs) with :param:
{ path: 'user/:id', component: UserDetailComponent }
Access the parameter in the component:
import { ActivatedRoute } from '@angular/router';
constructor(private route: ActivatedRoute) {
this.route.params.subscribe(params => {
console.log(params['id']); // Logs the user ID
});
}
Forms: Template-Driven vs. Reactive
Angular supports two approaches to building forms:
1. Template-Driven Forms
Simple, declarative, and ideal for basic forms. Uses ngModel for two-way binding.
Setup: Import FormsModule in app.module.ts.
Example:
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm.value)">
<input type="text" name="name" ngModel required>
<button type="submit" [disabled]="!userForm.valid">Submit</button>
</form>
In the component:
onSubmit(formData: any) {
console.log('Form submitted:', formData); // { name: "John" }
}
2. Reactive Forms
More powerful and testable, with explicit control over form state. Uses FormGroup and FormControl.
Setup: Import ReactiveFormsModule in app.module.ts.
Example:
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({ ... })
export class UserFormComponent {
userForm: FormGroup;
constructor(private fb: FormBuilder) {
this.userForm = this.fb.group({
name: ['', Validators.required], // Initial value + validators
email: ['', [Validators.required, Validators.email]]
});
}
onSubmit() {
if (this.userForm.valid) {
console.log('Form data:', this.userForm.value);
}
}
}
Template:
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
<input formControlName="name">
<input formControlName="email">
<button type="submit" [disabled]="!userForm.valid">Submit</button>
</form>
HTTP Client: Fetching & Sending Data
The HttpClientModule enables communication with APIs.
Setup:
Import HttpClientModule in app.module.ts.
Example: Fetch Data from an API
Create a service to handle HTTP requests:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class DataService {
constructor(private http: HttpClient) { }
fetchUsers(): Observable<any[]> {
return this.http.get<any[]>('https://jsonplaceholder.typicode.com/users');
}
createUser(user: any): Observable<any> {
return this.http.post('https://jsonplaceholder.typicode.com/users', user);
}
}
Use the service in a component:
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';
@Component({ ... })
export class UserListComponent implements OnInit {
users: any[] = [];
constructor(private dataService: DataService) { }
ngOnInit() {
this.dataService.fetchUsers().subscribe({
next: (data) => this.users = data,
error: (err) => console.error('Error fetching users:', err)
});
}
}
State Management
For large apps, managing state (e.g., user sessions, app data) requires a centralized solution.
NgRx (Redux for Angular)
NgRx uses a store to manage state, with actions, reducers, and effects for side effects (e.g., API calls).
Key Concepts:
- Store: Holds the app’s state.
- Actions: Describe state changes (e.g.,
LoadUsers,UsersLoaded). - Reducers: Pure functions that update state in response to actions.
- Effects: Handle async operations (e.g., API calls) and dispatch new actions.
Example: Dispatch an Action
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { loadUsers } from './store/user.actions';
@Component({ ... })
export class UserComponent {
constructor(private store: Store) {
this.store.dispatch(loadUsers()); // Dispatch action to load users
}
}
Testing Angular Apps
Angular provides tools for unit testing (Jasmine/Karma) and end-to-end (E2E) testing (Cypress/Protractor).
Unit Testing with Jasmine & Karma
Test components, services, and pipes in isolation.
Example: Test a Service
import { TestBed } from '@angular/core/testing';
import { DataService } from './data.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
describe('DataService', () => {
let service: DataService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [DataService]
});
service = TestBed.inject(DataService);
httpMock = TestBed.inject(HttpTestingController);
});
it('should fetch users', () => {
const mockUsers = [{ id: 1, name: 'John' }];
service.fetchUsers().subscribe(users => {
expect(users).toEqual(mockUsers);
});
const req = httpMock.expectOne('https://jsonplaceholder.typicode.com/users');
expect(req.request.method).toBe('GET');
req.flush(mockUsers);
});
});
Run tests with:
ng test
Deployment
To deploy an Angular app:
Step 1: Build for Production
ng build --configuration production # Generates optimized files in `dist/` folder
Step 2: Deploy to a Hosting Service
- Firebase:
firebase deploy(after setting up Firebase CLI). - Netlify/Vercel: Drag-and-drop the
dist/folder or connect to a Git repo. - AWS S3: Upload the
dist/folder to an S3 bucket configured for static website hosting.
Best Practices
- Lazy Loading Modules: Load non-critical modules on demand to reduce initial load time:
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) } - Avoid Memory Leaks: Unsubscribe from Observables in
ngOnDestroy(). - Use
trackBywith*ngFor: Improve performance for large lists:<li *ngFor="let item of items; trackBy: trackById">{{ item.name }}</li>trackById(index: number, item: any): number { return item.id; } - Optimize Bundle Size: Use
ng build --stats-jsonto analyze bundles and remove unused code with tree shaking.
Advanced Topics
- Angular Universal: Server-side rendering (SSR) for better SEO and performance.
- Progressive Web Apps (PWA): Add service workers with
ng add @angular/pwafor offline support. - Internationalization (i18n): Localize apps using
@angular/localize. - Micro Frontends: Split large apps into smaller, independent Angular apps.
Conclusion
Angular is a versatile framework for building modern web applications. By mastering its core concepts—components, modules, routing, forms, and HTTP—you’ll be able to create scalable, maintainable apps. Remember, practice is key: build projects, experiment with advanced features, and refer to the Angular documentation for updates.