Angular — Frontend Framework by Google
Angular is the full-stack frontend framework used in TCS ILP PRA and Sprint 4. It is a component-based framework written in TypeScript that builds Single Page Applications (SPAs). You will build a complete Angular frontend that connects to your Spring Boot backend.
Sprint 4: You build a full Angular frontend with routing, forms, HTTP calls to your REST API.
1. What is Angular?
Angular is an open-source frontend framework by Google for building SPAs. An SPA loads a single HTML page and dynamically updates content without full page reloads — making apps feel fast like desktop applications.
SPA (Single Page Application) — What It Means
Traditional websites: click a link → browser loads a completely new page from the server. SPA: click a link → JavaScript replaces only the changed part of the page. No full reload. Gmail, Google Maps, Netflix — all SPAs.
| Feature | Traditional Website | SPA (Angular) |
|---|---|---|
| Page load | Full reload on every click | One initial load, then partial updates |
| Speed | Slower (server round-trip each time) | Faster after initial load |
| Server role | Renders HTML | Sends JSON data via REST API |
| Example | Wikipedia | Gmail, Netflix |
Angular vs React vs Vue — Comparison
| Feature | Angular | React | Vue |
|---|---|---|---|
| Type | Full framework | Library (UI only) | Progressive framework |
| Language | TypeScript | JavaScript/JSX | JavaScript |
| Created by | Facebook (Meta) | Evan You | |
| Data binding | Two-way | One-way | Two-way |
| DOM | Real DOM + Change Detection | Virtual DOM | Virtual DOM |
| CLI tool | Angular CLI | Create React App / Vite | Vue CLI / Vite |
| Learning curve | Steeper | Moderate | Easiest |
2. Angular CLI
The Angular CLI (Command Line Interface) is a tool that creates projects, generates code, runs dev servers, and builds your app. You will use it every day in Sprint 4.
Installation
# Install Angular CLI globally (needs Node.js + npm installed first) npm install -g @angular/cli # Check version ng version
Essential Commands
| Command | What It Does |
|---|---|
ng new project-name | Creates a new Angular project with all files |
ng serve | Starts dev server at localhost:4200 |
ng generate component name | Creates a new component (4 files) |
ng g c name | Short form of above |
ng generate service name | Creates a new service |
ng g s name | Short form of above |
ng build | Builds production-ready files in dist/ folder |
ng test | Runs unit tests |
ng serve is 4200. If exam asks "what port does Angular dev server use?" — answer is 4200.
Project Structure (What ng new Creates)
my-app/ ├── src/ │ ├── app/ │ │ ├── app.component.ts ← root component (TypeScript) │ │ ├── app.component.html ← root template (HTML) │ │ ├── app.component.css ← root styles │ │ ├── app.component.spec.ts ← root test file │ │ └── app.module.ts ← root module │ ├── assets/ ← images, fonts, etc. │ ├── index.html ← single HTML page (SPA!) │ ├── main.ts ← entry point — bootstraps AppModule │ └── styles.css ← global styles ├── angular.json ← Angular project config ├── package.json ← npm dependencies ├── tsconfig.json ← TypeScript config └── node_modules/ ← installed packages
.ts (logic), .html (template), .css (styles), .spec.ts (test). This is a common exam question.
3. Angular Architecture
Angular apps are built from a few core building blocks. Understanding how they connect is crucial for the exam.
The Building Blocks
| Building Block | What It Does | Decorator |
|---|---|---|
| Module | Organizes the app into chunks. Every app has at least one (AppModule). | @NgModule |
| Component | Controls a piece of the UI (a view). Has template + logic + styles. | @Component |
| Template | The HTML with Angular syntax (bindings, directives). | — |
| Service | Shared business logic, data access, API calls. | @Injectable |
| Directive | Adds behavior to DOM elements. | @Directive |
| Pipe | Transforms data for display. | @Pipe |
How They Connect
// Flow: Module contains Components. Components use Templates. // Components inject Services. Templates use Directives and Pipes. ┌──────────────┐ │ @NgModule │ ← organizes everything │ (AppModule) │ └──────┬───────┘ │ declares ┌──────▼───────┐ │ @Component │ ← controls a view │ (AppComp) │ └──┬───────┬───┘ │ │ ┌─────▼──┐ ┌─▼──────────┐ │Template │ │ @Injectable │ ← injected via constructor │ (HTML) │ │ (Service) │ └────┬────┘ └─────────────┘ │ uses ┌────▼──────────────┐ │ Directives, Pipes │ └───────────────────┘
new. You declare the service in the constructor, and Angular provides the instance. This makes code testable and reusable.
4. Components (CORE TOPIC)
Components are the most important building block of Angular. Every piece of UI is a component — the header, sidebar, product card, login form — everything. A component = TypeScript class + HTML template + CSS styles.
Creating a Component
// Terminal: generates component files ng generate component user-profile // Short form: ng g c user-profile
This creates 4 files:
src/app/user-profile/ ├── user-profile.component.ts ← component class ├── user-profile.component.html ← template ├── user-profile.component.css ← styles └── user-profile.component.spec.ts ← test
Component Anatomy (The @Component Decorator)
// user-profile.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-user-profile', // ← HTML tag name to use this component templateUrl: './user-profile.component.html', // ← path to HTML styleUrls: ['./user-profile.component.css'] // ← path to CSS }) export class UserProfileComponent { name: string = 'Darshan'; // ← data property age: number = 22; greet(): string { // ← method return `Hello, ${this.name}!`; } }
<!-- user-profile.component.html --> <h2>{{ name }}</h2> <!-- interpolation: shows "Darshan" --> <p>Age: {{ age }}</p> <p>{{ greet() }}</p> <!-- can call methods too -->
<!-- Using the component in another template --> <app-user-profile></app-user-profile> <!-- the selector becomes a custom HTML tag -->
selector — the custom HTML tag nametemplateUrl — path to the HTML file (or template for inline HTML)styleUrls — array of CSS file paths (or styles for inline CSS)
Component Lifecycle Hooks
Angular calls special methods (hooks) at different stages of a component's life. You don't call these — Angular calls them automatically.
| Hook | When It Runs | Common Use |
|---|---|---|
constructor() | When component class is created (before Angular does anything) | Inject services only — no logic here |
ngOnChanges() | When an @Input() property value changes | React to input changes |
ngOnInit() | After first ngOnChanges — component is ready | Fetch data, initialize logic — MOST USED |
ngDoCheck() | Every change detection cycle | Custom change detection |
ngAfterContentInit() | After projected content is initialized | Rarely used |
ngAfterContentChecked() | After projected content is checked | Rarely used |
ngAfterViewInit() | After component's view is initialized | Access DOM elements |
ngAfterViewChecked() | After component's view is checked | Rarely used |
ngOnDestroy() | Just before component is removed | Unsubscribe, cleanup timers |
Mnemonic: Construct → Changes → Init → DoCheck → Content(Init/Checked) → View(Init/Checked) → Destroy
Using ngOnInit (Most Important Hook)
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-user-list', templateUrl: './user-list.component.html' }) export class UserListComponent implements OnInit { users: string[] = []; // Angular calls this automatically after component initializes ngOnInit(): void { // Fetch data, set up initial values this.users = ['Alice', 'Bob', 'Charlie']; console.log('Component initialized!'); } }
5. Data Binding (4 Types — VERY IMPORTANT)
Data binding is how your TypeScript component class communicates with the HTML template. Angular has 4 types. This is one of the most tested topics.
| # | Type | Syntax | Direction | Example |
|---|---|---|---|---|
| 1 | Interpolation | {{ expression }} | Component → Template | {{ name }} |
| 2 | Property Binding | [property]="expr" | Component → Template | [src]="imageUrl" |
| 3 | Event Binding | (event)="handler()" | Template → Component | (click)="save()" |
| 4 | Two-Way Binding | [(ngModel)]="prop" | Both directions | [(ngModel)]="name" |
{{ }} — "mustache syntax"Property: Square brackets
[ ] — data flows IN to the elementEvent: Parentheses
( ) — data flows OUT from the elementTwo-way: Banana in a box
[( )] — both directions! (square + round brackets)
1. Interpolation — {{ expression }}
// Component (TypeScript) export class AppComponent { title = 'My Angular App'; price = 99.5; } <!-- Template (HTML) --> <h1>{{ title }}</h1> <!-- Output: My Angular App --> <p>Price: {{ price * 1.18 }}</p> <!-- Can do math: 117.41 --> <p>{{ title.length }}</p> <!-- Can access properties: 14 --> <p>{{ title.toUpperCase() }}</p> <!-- Can call methods -->
2. Property Binding — [property]="expression"
// Component export class AppComponent { imageUrl = 'assets/logo.png'; isDisabled = true; } <!-- Template --> <img [src]="imageUrl"> <!-- binds src attribute --> <button [disabled]="isDisabled">Click</button> <!-- disables button -->
<img src="{{ imageUrl }}"> works but <img [src]="imageUrl"> is preferred. For non-string attributes like [disabled], you MUST use property binding — interpolation won't work.
3. Event Binding — (event)="handler()"
// Component export class AppComponent { counter = 0; increment() { this.counter++; } onKeyPress(event: KeyboardEvent) { console.log(event.key); } } <!-- Template --> <button (click)="increment()">Count: {{ counter }}</button> <input (keyup)="onKeyPress($event)"> <!-- $event = the DOM event object -->
4. Two-Way Binding — [(ngModel)]="property"
// IMPORTANT: You must import FormsModule first! // In app.module.ts: import { FormsModule } from '@angular/forms'; @NgModule({ imports: [FormsModule], // ← add this! ... }) // Component export class AppComponent { username = ''; } <!-- Template --> <input [(ngModel)]="username"> <!-- typing updates username --> <p>Hello, {{ username }}!</p> <!-- shows what you type, live -->
[(ngModel)] requires FormsModule to be imported in app.module.ts. Without it, you get an error. Exam often asks "what module is needed for ngModel?" — answer: FormsModule.
6. Directives
Directives are instructions that tell Angular to modify the DOM. There are two main types: structural (add/remove elements) and attribute (change appearance/behavior).
Structural Directives (Modify the DOM Structure)
These start with an asterisk * — that's how you spot them. They add or remove elements from the page.
*ngIf — Show/hide based on condition
<!-- Only shows if isLoggedIn is true --> <div *ngIf="isLoggedIn">Welcome back, user!</div> <!-- if-else pattern --> <div *ngIf="isLoggedIn; else loginTemplate"> Welcome back! </div> <ng-template #loginTemplate> <p>Please log in.</p> </ng-template>
*ngFor — Loop through a list
// Component fruits: string[] = ['Apple', 'Banana', 'Cherry']; <!-- Template --> <ul> <li *ngFor="let fruit of fruits">{{ fruit }}</li> </ul> <!-- Output: Apple, Banana, Cherry as list items --> <!-- With index --> <li *ngFor="let fruit of fruits; let i = index"> {{ i + 1 }}. {{ fruit }} </li>
*ngSwitch — Multiple conditions
<div [ngSwitch]="color"> <p *ngSwitchCase="'red'">Stop!</p> <p *ngSwitchCase="'green'">Go!</p> <p *ngSwitchCase="'yellow'">Slow down!</p> <p *ngSwitchDefault>Unknown color</p> </div>
<div *ngIf="show" *ngFor="let item of items"> — will cause an error.Solution: wrap one in an
<ng-container>.
Attribute Directives (Modify Appearance/Behavior)
These change how an element looks or behaves — they don't add or remove elements.
ngClass — Add/remove CSS classes dynamically
<!-- Add 'active' class when isActive is true --> <div [ngClass]="{'active': isActive, 'disabled': !isActive}"> Status Box </div> <!-- Using a string --> <div [ngClass]="currentClass">Styled div</div>
ngStyle — Add inline styles dynamically
<!-- Set color and font-size dynamically -->
<p [ngStyle]="{'color': textColor, 'font-size': fontSize + 'px'}">
Styled text
</p>
*ngIf, *ngFor, *ngSwitch — they start with *, they change the DOM structure.Attribute:
ngClass, ngStyle — they change element properties, NOT the DOM structure.If exam asks "which is a structural directive?" — look for the asterisk
*.
7. Services and Dependency Injection
A service is a TypeScript class that holds business logic, data, or API calls — anything that isn't directly about the UI. Components should be thin (just UI logic); heavy work goes in services.
Creating a Service
# Terminal ng generate service user # Creates: user.service.ts, user.service.spec.ts
Service Code
// user.service.ts import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' // ← makes it a singleton, available everywhere }) export class UserService { private users: string[] = ['Alice', 'Bob', 'Charlie']; getUsers(): string[] { return this.users; } addUser(name: string): void { this.users.push(name); } }
Injecting a Service into a Component
// user-list.component.ts import { Component, OnInit } from '@angular/core'; import { UserService } from '../user.service'; @Component({ selector: 'app-user-list', templateUrl: './user-list.component.html' }) export class UserListComponent implements OnInit { users: string[] = []; // Service is injected via constructor — this is DI! constructor(private userService: UserService) {} ngOnInit(): void { this.users = this.userService.getUsers(); } }
@Injectable({ providedIn: 'root' }) means:1. Angular creates one single instance (singleton) of this service for the entire app.
2. You don't need to add it to any module's
providers array — it's auto-registered.3. All components that inject it get the same instance — shared data!
Reusability: Multiple components can share the same service.
Testability: Services can be tested independently.
8. Routing
Routing lets you navigate between different views (components) in your SPA without reloading the page. When you click a link, Angular swaps components — the URL changes, but the page doesn't reload.
Setting Up Routes
// app-routing.module.ts (or app.routes.ts) import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './home/home.component'; import { AboutComponent } from './about/about.component'; import { UserComponent } from './user/user.component'; import { NotFoundComponent } from './not-found/not-found.component'; const routes: Routes = [ { path: '', component: HomeComponent }, // localhost:4200/ { path: 'about', component: AboutComponent }, // localhost:4200/about { path: 'users/:id', component: UserComponent }, // localhost:4200/users/5 { path: '**', component: NotFoundComponent } // any unmatched URL → 404 ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}
Using Routes in Templates
<!-- app.component.html --> <!-- Navigation links --> <nav> <a routerLink="/">Home</a> <a routerLink="/about">About</a> <a routerLink="/users/1">User 1</a> </nav> <!-- This is where the routed component appears --> <router-outlet></router-outlet>
Reading Route Parameters
// user.component.ts — reading :id from the URL import { ActivatedRoute } from '@angular/router'; export class UserComponent implements OnInit { userId: string = ''; constructor(private route: ActivatedRoute) {} ngOnInit(): void { this.userId = this.route.snapshot.params['id']; // For URL /users/5, this.userId = "5" } }
Programmatic Navigation
import { Router } from '@angular/router'; export class LoginComponent { constructor(private router: Router) {} onLoginSuccess() { this.router.navigate(['/dashboard']); // redirect after login } }
href)router-outlet = placeholder where the routed component gets displayed
ActivatedRoute = service to read route parameters
Router = service for programmatic navigation
path: '**' = wildcard route for 404 pages (must be LAST in the array)
9. HTTP Client
Angular's HttpClient lets you make API calls (GET, POST, PUT, DELETE) to your backend (like your Spring Boot REST API). It returns Observables, which you subscribe to.
Setup
// app.module.ts — import HttpClientModule import { HttpClientModule } from '@angular/common/http'; @NgModule({ imports: [HttpClientModule], // ← add this ... })
Making HTTP Calls in a Service
// user.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; interface User { id: number; name: string; email: string; } @Injectable({ providedIn: 'root' }) export class UserService { private apiUrl = 'http://localhost:8080/api/users'; constructor(private http: HttpClient) {} // GET — fetch all users getUsers(): Observable<User[]> { return this.http.get<User[]>(this.apiUrl); } // GET — fetch one user by ID getUserById(id: number): Observable<User> { return this.http.get<User>(`${this.apiUrl}/${id}`); } // POST — create new user createUser(user: User): Observable<User> { return this.http.post<User>(this.apiUrl, user); } // PUT — update existing user updateUser(id: number, user: User): Observable<User> { return this.http.put<User>(`${this.apiUrl}/${id}`, user); } // DELETE — remove user deleteUser(id: number): Observable<void> { return this.http.delete<void>(`${this.apiUrl}/${id}`); } }
Using the Service in a Component
// user-list.component.ts export class UserListComponent implements OnInit { users: User[] = []; constructor(private userService: UserService) {} ngOnInit(): void { // subscribe() triggers the HTTP call and handles the response this.userService.getUsers().subscribe({ next: (data) => this.users = data, error: (err) => console.error('Error:', err) }); } }
subscribe() = tells Angular "I want the data — execute the HTTP call now."
Without calling
.subscribe(), the HTTP request is never sent. This is a critical difference from Promises. Observables are lazy — they only execute when subscribed to.
"HTTP methods return what?" — Observables (not Promises)
"What happens if you don't call subscribe()?" — Nothing. The request is never made.
10. Pipes
Pipes transform data for display in templates. They don't change the actual data — just how it looks. Think of them as display formatters.
Syntax: {{ value | pipeName }}
Built-in Pipes
| Pipe | Input | Output | Example |
|---|---|---|---|
uppercase | 'hello' | 'HELLO' | {{ name | uppercase }} |
lowercase | 'HELLO' | 'hello' | {{ name | lowercase }} |
titlecase | 'hello world' | 'Hello World' | {{ name | titlecase }} |
date | Date object | Formatted date | {{ today | date:'fullDate' }} |
currency | 99.5 | $99.50 | {{ price | currency:'INR' }} |
percent | 0.85 | 85% | {{ score | percent }} |
json | Object | JSON string | {{ user | json }} |
slice | Array/string | Sliced portion | {{ name | slice:0:5 }} |
Chaining Pipes
<!-- Apply multiple pipes left to right --> {{ name | uppercase | slice:0:5 }} <!-- "darshan" → "DARSHAN" → "DARSH" --> {{ birthday | date:'shortDate' }} <!-- Date object → "3/22/26" --> {{ salary | currency:'INR':'symbol':'1.0-0' }} <!-- 50000 → "₹50,000" -->
Custom Pipe
// reverse.pipe.ts import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'reverse' }) export class ReversePipe implements PipeTransform { transform(value: string): string { return value.split('').reverse().join(''); } } <!-- Usage: {{ 'hello' | reverse }} → "olleh" -->
| in templates. Custom pipes need the @Pipe decorator and must implement PipeTransform. Know the built-in pipes — date, uppercase, currency, json are the exam favorites.
11. Forms
Angular has two ways to build forms. Both work — they just differ in where the form logic lives.
Comparison
| Feature | Template-Driven Forms | Reactive Forms |
|---|---|---|
| Logic lives in | Template (HTML) | Component (TypeScript) |
| Module needed | FormsModule | ReactiveFormsModule |
| Binding | ngModel | formControlName |
| Validation | HTML attributes (required, minlength) | Validators in TS code |
| Best for | Simple forms | Complex, dynamic forms |
| Testing | Harder to unit test | Easy to unit test |
Template-Driven Form
<!-- Import FormsModule in app.module.ts first --> <form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)"> <input type="text" name="username" ngModel required minlength="3"> <input type="email" name="email" ngModel required> <button type="submit" [disabled]="myForm.invalid">Submit</button> </form> <!-- Validation message --> <div *ngIf="myForm.controls['username']?.errors?.['required']"> Username is required </div>
Reactive Form
// Component (TypeScript) import { FormGroup, FormControl, Validators } from '@angular/forms'; export class RegisterComponent { registerForm = new FormGroup({ username: new FormControl('', [ Validators.required, Validators.minLength(3) ]), email: new FormControl('', [ Validators.required, Validators.email ]), password: new FormControl('', [ Validators.required, Validators.minLength(6) ]) }); onSubmit() { if (this.registerForm.valid) { console.log(this.registerForm.value); // { username: 'darshan', email: 'a@b.com', password: '123456' } } } } <!-- Template --> <form [formGroup]="registerForm" (ngSubmit)="onSubmit()"> <input formControlName="username" placeholder="Username"> <input formControlName="email" placeholder="Email"> <input formControlName="password" type="password" placeholder="Password"> <button [disabled]="registerForm.invalid">Register</button> </form>
FormsModule + ngModel. Reactive uses ReactiveFormsModule + FormGroup/FormControl. Know which module goes with which approach — this is a common question.
12. Modules (@NgModule)
Modules organize your Angular app into logical blocks. Every Angular app has at least one module — the root module (AppModule).
@NgModule Properties
@NgModule({ declarations: [ // Components, directives, pipes that BELONG to this module AppComponent, UserListComponent, UserDetailComponent ], imports: [ // Other modules this module NEEDS BrowserModule, FormsModule, HttpClientModule, AppRoutingModule ], providers: [ // Services (if not using providedIn: 'root') UserService ], bootstrap: [ // The root component that starts the app AppComponent ] })
| Property | What Goes Here |
|---|---|
declarations | Components, directives, pipes created in THIS module |
imports | Other modules this module depends on (FormsModule, HttpClientModule, etc.) |
providers | Services (older way — prefer providedIn: 'root' instead) |
bootstrap | The root component (only in AppModule — usually AppComponent) |
exports | Components/directives/pipes available to other modules that import this one |
UserModule, ProductModule, AdminModule. Each has its own components, services, and routes. Lazy loading means a feature module is only loaded when the user navigates to its route — making the initial load faster.
index.html. Only the root module (AppModule) has this. Answer is always AppComponent.
13. Angular vs React — Quick Comparison
| Feature | Angular | React |
|---|---|---|
| Type | Framework (full solution) | Library (UI only) |
| Language | TypeScript (mandatory) | JavaScript (TypeScript optional) |
| Data binding | Two-way (ngModel) | One-way (top-down) |
| DOM | Real DOM + zones | Virtual DOM |
| Routing | Built-in (RouterModule) | External (React Router) |
| HTTP | Built-in (HttpClient) | External (Axios/fetch) |
| Forms | Built-in (FormsModule) | External (Formik, React Hook Form) |
| CLI | Angular CLI (ng) | Create React App / Vite |
| Architecture | Opinionated (one way to do things) | Flexible (many choices) |
| Learning curve | Steeper | Moderate |
| Created by | Facebook (Meta) |
14. Practice MCQs
ng serve?[(ngModel)]?providedIn: 'root' do in an @Injectable decorator?<router-outlet></router-outlet> is the placeholder in the template where Angular renders the matched route's component.name in an Angular template?{ path: '**' } do?'app-user-profile') used to place this component in other templates.50000 as ₹50,000.00?{{ 50000 | currency:'INR' }}. The currency pipe formats numbers with the specified currency symbol.ng generate component user create?