Angular Mastery Guide
Complete guide from components to advanced patterns
The Complete Angular Mastery Guide
Master enterprise-grade web development with Angular's comprehensive framework, TypeScript integration, and powerful tooling
Understanding Angular: The Enterprise Framework
Angular is a platform and framework for building single-page client applications using HTML and TypeScript. Developed and maintained by Google, Angular provides a comprehensive solution for building large-scale, enterprise-grade applications with strong typing, dependency injection, and a powerful CLI.
What makes Angular unique is its opinionated, batteries-included approach that provides everything you need out of the box - routing, forms, HTTP client, testing utilities, and more - all integrated seamlessly with TypeScript.
Enterprise Standard: Angular is used by companies like Google, Microsoft, IBM, and Forbes. Its comprehensive nature, strong typing, and enterprise-focused features make it the framework of choice for large teams building complex applications.
1. Angular Basics & Components
Getting Started: Your First Angular App
Angular applications are built using components, modules, and services. The Angular CLI makes it easy to create and manage projects.
// Install Angular CLI
npm install -g @angular/cli
// Create new project
ng new my-app
cd my-app
ng serve
// Basic component structure
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>Welcome to {{title}}!</h1>
<app-hero-list></app-hero-list>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'My Angular App';
}
// Module definition
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HeroListComponent } from './hero-list.component';
@NgModule({
declarations: [
AppComponent,
HeroListComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// Main entry point
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
Components: Building Blocks of Angular
Components are the fundamental building blocks of Angular applications. They control a patch of screen called a view.
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';
// Hero interface
export interface Hero {
id: number;
name: string;
power: string;
}
// Hero list component
@Component({
selector: 'app-hero-list',
standalone: true,
imports: [CommonModule, HeroDetailComponent],
template: `
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes"
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<app-hero-detail
[hero]="selectedHero"
(heroSaved)="onHeroSaved($event)">
</app-hero-detail>
`,
styles: [`
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
}
.heroes li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
border-radius: 4px;
}
.heroes li.selected {
background-color: #CFD8DC;
color: white;
}
`]
})
export class HeroListComponent {
@Input() heroes: Hero[] = [];
@Output() heroSelected = new EventEmitter<Hero>();
selectedHero?: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
this.heroSelected.emit(hero);
}
onHeroSaved(hero: Hero): void {
console.log('Hero saved:', hero);
// Update the hero in the list
const index = this.heroes.findIndex(h => h.id === hero.id);
if (index !== -1) {
this.heroes[index] = hero;
}
}
}
// Hero detail component
@Component({
selector: 'app-hero-detail',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<div *ngIf="hero">
<h2>{{hero.name}} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div>
<label for="hero-name">Hero name: </label>
<input id="hero-name" [(ngModel)]="hero.name" placeholder="name">
</div>
<div>
<label for="hero-power">Power: </label>
<input id="hero-power" [(ngModel)]="hero.power" placeholder="power">
</div>
<button (click)="save()">Save</button>
</div>
`
})
export class HeroDetailComponent {
@Input() hero?: Hero;
@Output() heroSaved = new EventEmitter<Hero>();
save(): void {
if (this.hero) {
this.heroSaved.emit(this.hero);
}
}
}
// Standalone component (Angular 14+)
@Component({
selector: 'app-standalone-hero',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<div class="standalone-hero">
<h3>{{title}}</h3>
<p>This is a standalone component!</p>
</div>
`
})
export class StandaloneHeroComponent {
title = 'Standalone Hero Component';
}
// Component lifecycle hooks
@Component({
selector: 'app-lifecycle-demo',
template: `
<div>
<h3>Lifecycle Demo</h3>
<button (click)="toggle()">Toggle</button>
<div *ngIf="showContent">Content to show/hide</div>
</div>
`
})
export class LifecycleDemoComponent implements
OnInit, OnChanges, OnDestroy, AfterViewInit, AfterContentInit {
@Input() data: any;
showContent = true;
constructor() {
console.log('Constructor called');
}
ngOnInit(): void {
console.log('OnInit - Component initialized');
}
ngOnChanges(changes: SimpleChanges): void {
console.log('OnChanges - Input properties changed', changes);
}
ngAfterViewInit(): void {
console.log('AfterViewInit - View initialized');
}
ngAfterContentInit(): void {
console.log('AfterContentInit - Content projected');
}
ngOnDestroy(): void {
console.log('OnDestroy - Component destroyed');
}
toggle(): void {
this.showContent = !this.showContent;
}
}
Modules and Application Structure
Angular applications are modular, organized into NgModules that consolidate related code into functional sets.
// Core module for singleton services
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { AuthService } from './auth.service';
import { UserService } from './user.service';
import { throwIfAlreadyLoaded } from './module-import-guard';
@NgModule({
imports: [
CommonModule,
HttpClientModule
],
providers: [
AuthService,
UserService
]
})
export class CoreModule {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
throwIfAlreadyLoaded(parentModule, 'CoreModule');
}
}
// Shared module for common components, directives, pipes
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HighlightDirective } from './highlight.directive';
import { UnlessDirective } from './unless.directive';
import { FilterPipe } from './filter.pipe';
import { LoadingSpinnerComponent } from './loading-spinner.component';
@NgModule({
imports: [
CommonModule,
FormsModule
],
declarations: [
HighlightDirective,
UnlessDirective,
FilterPipe,
LoadingSpinnerComponent
],
exports: [
CommonModule,
FormsModule,
HighlightDirective,
UnlessDirective,
FilterPipe,
LoadingSpinnerComponent
]
})
export class SharedModule { }
// Feature module for heroes functionality
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroSearchComponent } from './hero-search.component';
import { HeroService } from './hero.service';
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule.forChild([
{ path: 'heroes', component: HeroListComponent },
{ path: 'hero/:id', component: HeroDetailComponent }
])
],
declarations: [
HeroListComponent,
HeroDetailComponent,
HeroSearchComponent
],
providers: [
HeroService
]
})
export class HeroesModule { }
// Main app module
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
import { SharedModule } from './shared/shared.module';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
CoreModule,
SharedModule,
AppRoutingModule,
// Feature modules (lazy loaded)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// Routing module
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PageNotFoundComponent } from './page-not-found.component';
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{
path: 'heroes',
loadChildren: () => import('./heroes/heroes.module').then(m => m.HeroesModule)
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
},
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
enableTracing: false, // For debugging
preloadingStrategy: PreloadAllModules
})
],
exports: [RouterModule]
})
export class AppRoutingModule { }
// Module import guard
export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) {
if (parentModule) {
throw new Error(`${moduleName} has already been loaded. Import Core modules in the AppModule only.`);
}
}
Directives and Pipes
Directives add behavior to elements in the DOM, while pipes transform displayed values in templates.
// Attribute directive
import { Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlight]',
standalone: true
})
export class HighlightDirective {
@Input() appHighlight = '';
@Input() defaultColor = '';
constructor(private el: ElementRef, private renderer: Renderer2) {}
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.appHighlight || this.defaultColor || 'yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight('');
}
private highlight(color: string) {
this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', color);
}
}
// Structural directive
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appUnless]',
standalone: true
})
export class UnlessDirective {
private hasView = false;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) { }
@Input() set appUnless(condition: boolean) {
if (!condition && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
}
// Custom pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'appFilter',
standalone: true
})
export class FilterPipe implements PipeTransform {
transform(items: any[], searchText: string, field: string = 'name'): any[] {
if (!items) return [];
if (!searchText) return items;
searchText = searchText.toLowerCase();
return items.filter(item => {
return item[field].toLowerCase().includes(searchText);
});
}
}
// Async pipe alternative with loading state
@Pipe({
name: 'appWithLoading',
standalone: true
})
export class WithLoadingPipe implements PipeTransform {
transform(value: any): { data: any, loading: boolean, error: any } {
if (value instanceof Error) {
return { data: null, loading: false, error: value };
}
if (value === null || value === undefined) {
return { data: null, loading: true, error: null };
}
return { data: value, loading: false, error: null };
}
}
// Currency pipe with custom formatting
@Pipe({
name: 'appCustomCurrency',
standalone: true
})
export class CustomCurrencyPipe implements PipeTransform {
transform(value: number, currencyCode: string = 'USD', display: 'symbol' | 'code' | 'name' = 'symbol'): string {
if (value == null) return '';
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currencyCode,
currencyDisplay: display,
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
return formatter.format(value);
}
}
// Using directives and pipes in templates
@Component({
selector: 'app-directive-demo',
standalone: true,
imports: [CommonModule, HighlightDirective, UnlessDirective, FilterPipe, CustomCurrencyPipe],
template: `
<div class="demo-container">
<!-- Attribute directive -->
<p [appHighlight]="'lightblue'" [defaultColor]="'violet'">
Hover over me to see highlight!
</p>
<!-- Structural directive -->
<div *appUnless="isHidden">
This content is shown unless isHidden is true
</div>
<!-- Custom pipe -->
<input [(ngModel)]="searchText" placeholder="Search heroes">
<ul>
<li *ngFor="let hero of heroes | appFilter:searchText">
{{hero.name}}
</li>
</ul>
<!-- Currency pipe -->
<p>Price: {{price | appCustomCurrency:'EUR':'symbol'}}</p>
<p>Price: {{price | appCustomCurrency:'USD':'code'}}</p>
<!-- With loading pipe -->
<div *ngIf="data$ | async as result">
<div *ngIf="result.loading">Loading...</div>
<div *ngIf="result.error">Error: {{result.error.message}}</div>
<div *ngIf="result.data">
Data: {{result.data | json}}
</div>
</div>
</div>
`
})
export class DirectiveDemoComponent {
isHidden = false;
searchText = '';
price = 1234.56;
heroes = [
{ id: 1, name: 'Superman' },
{ id: 2, name: 'Batman' },
{ id: 3, name: 'Wonder Woman' }
];
data$ = of([1, 2, 3]).pipe(
delay(1000),
catchError(err => of(new Error('Failed to load')))
);
}
2. Templates & Data Binding
Data Binding: Connecting Component and Template
Angular provides several ways to bind data between components and templates, including interpolation, property binding, event binding, and two-way binding.
@Component({
selector: 'app-data-binding-demo',
template: `
<div class="binding-demo">
<!-- Interpolation -->
<h2>Hello, {{name}}!</h2>
<p>1 + 1 = {{1 + 1}}</p>
<p>Today is {{getCurrentDate() | date:'fullDate'}}</p>
<!-- Property binding -->
<img [src]="imageUrl" [alt]="imageAlt" [class.loaded]="isImageLoaded">
<button [disabled]="isDisabled">Click me</button>
<div [style.background-color]="backgroundColor"></div>
<div [ngClass]="{'active': isActive, 'error': hasError}"></div>
<!-- Attribute binding -->
<button [attr.aria-label]="ariaLabel">Action</button>
<div [attr.data-user-id]="userId"></div>
<!-- Class binding -->
<div [class.special]="isSpecial">Special content</div>
<div [class]="classExpression">Dynamic classes</div>
<!-- Style binding -->
<div [style.width.px]="width">Width: {{width}}px</div>
<div [style]="styleExpression">Dynamic styles</div>
<!-- Event binding -->
<button (click)="onButtonClick($event)">Click me!</button>
<input (keyup)="onKeyUp($event)" (keyup.enter)="onEnter()">
<div (mouseover)="onMouseOver()" (mouseleave)="onMouseLeave()">Hover me</div>
<!-- Two-way binding -->
<input [(ngModel)]="userName" placeholder="Enter your name">
<p>Hello, {{userName}}!</p>
<select [(ngModel)]="selectedOption">
<option *ngFor="let option of options" [value]="option">
{{option}}
</option>
</select>
<!-- Template reference variables -->
<input #phone placeholder="Phone number">
<button (click)="callPhone(phone.value)">Call</button>
<!-- ViewChild access -->
<div #container>Container content</div>
</div>
`
})
export class DataBindingDemoComponent implements AfterViewInit {
// Interpolation properties
name = 'Angular Developer';
// Property binding properties
imageUrl = '/assets/hero.jpg';
imageAlt = 'Hero image';
isImageLoaded = true;
isDisabled = false;
backgroundColor = 'lightblue';
isActive = true;
hasError = false;
// Attribute binding properties
ariaLabel = 'Primary action button';
userId = 'user123';
// Class binding properties
isSpecial = true;
classExpression = 'card highlighted';
// Style binding properties
width = 200;
styleExpression = 'font-size: 16px; color: red;';
// Event binding properties
userName = '';
selectedOption = '';
options = ['Option 1', 'Option 2', 'Option 3'];
// Template reference variables
@ViewChild('container') container!: ElementRef;
// Methods for event binding
onButtonClick(event: MouseEvent): void {
console.log('Button clicked!', event);
this.isDisabled = !this.isDisabled;
}
onKeyUp(event: KeyboardEvent): void {
console.log('Key pressed:', event.key);
}
onEnter(): void {
console.log('Enter key pressed!');
}
onMouseOver(): void {
this.backgroundColor = 'lightgreen';
}
onMouseLeave(): void {
this.backgroundColor = 'lightblue';
}
callPhone(phone: string): void {
console.log('Calling:', phone);
}
getCurrentDate(): Date {
return new Date();
}
ngAfterViewInit(): void {
// Access template reference variable
console.log('Container element:', this.container.nativeElement);
}
}
// Advanced data binding with signals (Angular 16+)
@Component({
selector: 'app-signals-demo',
template: `
<div class="signals-demo">
<!-- Signal binding -->
<h2>Counter: {{counter()}}</h2>
<p>Double: {{doubleCounter()}}</p>
<button (click)="increment()">Increment</button>
<button (click)="reset()">Reset</button>
<!-- Computed signals -->
<div *ngIf="isEven()">Counter is even!</div>
<div *ngIf="isOdd()">Counter is odd!</div>
<!-- Effect -->
<p>Previous value: {{previousValue()}}</p>
<!-- Signal in forms -->
<input [value]="name()" (input)="updateName($event)">
<p>Hello, {{name()}}!</p>
<!-- Signal arrays -->
<ul>
<li *ngFor="let item of items()">
{{item}}
<button (click)="removeItem(item)">Remove</button>
</li>
</ul>
<input #newItem placeholder="New item">
<button (click)="addItem(newItem.value); newItem.value=''">Add</button>
</div>
`
})
export class SignalsDemoComponent {
// Writable signals
counter = signal(0);
name = signal('Angular');
items = signal<string[]>(['Item 1', 'Item 2', 'Item 3']);
previousValue = signal<number | null>(null);
// Computed signals
doubleCounter = computed(() => this.counter() * 2);
isEven = computed(() => this.counter() % 2 === 0);
isOdd = computed(() => this.counter() % 2 !== 0);
constructor() {
// Effects
effect(() => {
const current = this.counter();
console.log('Counter changed:', current);
// Update previous value
this.previousValue.set(current - 1);
});
effect(() => {
const currentName = this.name();
console.log('Name changed:', currentName);
});
}
increment(): void {
this.counter.update(c => c + 1);
}
reset(): void {
this.counter.set(0);
}
updateName(event: Event): void {
const input = event.target as HTMLInputElement;
this.name.set(input.value);
}
addItem(item: string): void {
if (item.trim()) {
this.items.update(items => [...items, item]);
}
}
removeItem(item: string): void {
this.items.update(items => items.filter(i => i !== item));
}
}
Template Statements and Expressions
Template expressions and statements allow you to execute logic directly in templates while maintaining separation of concerns.
@Component({
selector: 'app-template-expressions',
template: `
<div class="expressions-demo">
<!-- Simple expressions -->
<p>Math: {{ 5 + 3 }}</p>
<p>Comparison: {{ 10 > 5 }}</p>
<p>String: {{ 'Hello' + ' ' + 'World' }}</p>
<!-- Property access -->
<p>Name: {{ user.name }}</p>
<p>First item: {{ items[0] }}</p>
<p>Array length: {{ items.length }}</p>
<!-- Method calls -->
<p>Uppercase: {{ 'hello'.toUpperCase() }}</p>
<p>Formatted date: {{ getFormattedDate() }}</p>
<p>Full name: {{ getFullName() }}</p>
<!-- Safe navigation operator -->
<p>User city: {{ user?.address?.city }}</p>
<p>First item: {{ items?.[0] }}</p>
<!-- Template statements -->
<button (click)="deleteItem(item)">Delete</button>
<input (keyup.enter)="onEnterPressed($event)">
<form (ngSubmit)="onSubmit()">...</form>
<!-- Complex expressions with pipes -->
<p>Price: {{ totalPrice | currency }}</p>
<p>Date: {{ currentDate | date:'medium' }}</p>
<p>Percentage: {{ completionRate | percent:'1.2-2' }}</p>
<!-- Chaining pipes -->
<p>Formatted: {{ message | uppercase | slice:0:10 }}</p>
<!-- Custom template logic -->
<ng-container *ngIf="isLoggedIn && hasPermission; else noAccess">
<p>Welcome, authorized user!</p>
</ng-container>
<ng-template #noAccess>
<p>Access denied</p>
</ng-template>
<!-- Ternary operator -->
<p [class]="isActive ? 'active' : 'inactive'">
Status: {{ isActive ? 'Active' : 'Inactive' }}
</p>
<!-- Template context -->
<ng-template #itemTemplate let-item let-index="index">
<div class="item">
<span>{{ index + 1 }}. {{ item.name }}</span>
<button (click)="selectItem(item)">Select</button>
</div>
</ng-template>
<!-- Using template with context -->
<ng-container
*ngTemplateOutlet="itemTemplate; context: { $implicit: currentItem, index: currentIndex }">
</ng-container>
</div>
`
})
export class TemplateExpressionsComponent {
user = {
name: 'John Doe',
address: {
city: 'New York',
country: 'USA'
}
};
items = ['Apple', 'Banana', 'Orange'];
currentItem = { name: 'Current Item' };
currentIndex = 0;
totalPrice = 99.99;
currentDate = new Date();
completionRate = 0.756;
message = 'Hello Angular World';
isLoggedIn = true;
hasPermission = true;
isActive = true;
getFormattedDate(): string {
return this.currentDate.toLocaleDateString();
}
getFullName(): string {
return `${this.user.name} from ${this.user.address?.city}`;
}
deleteItem(item: any): void {
console.log('Deleting item:', item);
// Implementation here
}
onEnterPressed(event: Event): void {
const input = event.target as HTMLInputElement;
console.log('Enter pressed with value:', input.value);
}
onSubmit(): void {
console.log('Form submitted');
}
selectItem(item: any): void {
console.log('Item selected:', item);
}
}
// Advanced template patterns
@Component({
selector: 'app-advanced-templates',
template: `
<div class="advanced-templates">
<!-- Content projection -->
<app-card>
<div header>Card Header</div>
<div body>
<p>This is the card body content.</p>
<p>It can contain any HTML elements.</p>
</div>
<div footer>Card Footer</div>
</app-card>
<!-- Multiple content projection -->
<app-layout>
<div sidebar>Sidebar Content</div>
<div main>Main Content Area</div>
<div toolbar>Toolbar Actions</div>
</app-layout>
<!-- Dynamic templates -->
<ng-container
*ngTemplateOutlet="currentTemplate; context: templateContext">
</ng-container>
<button (click)="switchTemplate()">Switch Template</button>
<!-- Template guards -->
<div *ngIf="data$ | async as data; loading: loadingTemplate; error: errorTemplate">
<p>Data loaded: {{ data | json }}</p>
</div>
<ng-template #loadingTemplate>
<p>Loading data...</p>
</ng-template>
<ng-template #errorTemplate let-error>
<p class="error">Error: {{ error.message }}</p>
</ng-template>
</div>
`
})
export class AdvancedTemplatesComponent {
currentTemplate: TemplateRef<any>;
templateContext = { message: 'Hello from context!' };
data$ = of({ id: 1, name: 'Test Data' }).pipe(
delay(2000)
);
constructor(private templateService: TemplateService) {
this.currentTemplate = this.templateService.getTemplate('default');
}
switchTemplate(): void {
this.currentTemplate = this.templateService.getTemplate('alternative');
}
}
// Card component with content projection
@Component({
selector: 'app-card',
template: `
<div class="card">
<div class="card-header">
<ng-content select="[header]"></ng-content>
</div>
<div class="card-body">
<ng-content select="[body]"></ng-content>
</div>
<div class="card-footer">
<ng-content select="[footer]"></ng-content>
</div>
</div>
`
})
export class CardComponent {}
// Layout component with multiple projection slots
@Component({
selector: 'app-layout',
template: `
<div class="layout">
<aside class="sidebar">
<ng-content select="[sidebar]"></ng-content>
</aside>
<main class="main-content">
<ng-content select="[main]"></ng-content>
</main>
<div class="toolbar">
<ng-content select="[toolbar]"></ng-content>
</div>
</div>
`
})
export class LayoutComponent {}
3. Services & Dependency Injection
Services and Dependency Injection
Services are singleton objects that provide functionality across your application. Angular's dependency injection system manages service instantiation and injection.
// Basic service
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // Tree-shakable service
})
export class DataService {
private data: any[] = [];
constructor() {
console.log('DataService created');
}
getData(): any
// app/nodejs-complete-guide/page.tsx
export default function NodeJSCompleteGuide() {
return (
<div className="min-h-screen bg-gradient-to-br from-green-50 to-emerald-50">
<div className="max-w-7xl mx-auto px-4 py-8">
{/* Mobile Header - Only shows on small screens */}
<header className="lg:hidden text-center mb-8">
<h1 className="text-3xl font-bold text-gray-800 mb-2">
Node.js Mastery Guide
</h1>
<p className="text-gray-600">
Complete guide from beginner to advanced
</p>
<div className="w-20 h-1 bg-green-500 mx-auto mt-2 rounded"></div>
</header>
<div className="flex flex-col md:flex-row gap-6 min-h-screen">
{/* Left Sidebar - Ads */}
<aside className="w-full md:w-[20%] order-1 md:order-1">
<div className="sticky top-6 md:block bg-gray-100 p-4 space-y-6">
{/* Left Ads Area */}
<p className="text-center">Left Ads</p>
</div>
</aside>
{/* Main Content */}
<main className="lg:w-3/5 order-1 lg:order-2">
{/* Desktop Header - Hidden on mobile */}
<header className="hidden lg:block text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-gray-800 mb-4">
The Complete Node.js Mastery Guide
</h1>
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
A comprehensive journey through Node.js fundamentals to advanced backend development and real-time applications.
</p>
<div className="w-24 h-1 bg-green-500 mx-auto mt-4 rounded"></div>
</header>
{/* Table of Contents */}
<nav className="bg-white rounded-lg shadow-md p-6 mb-8">
<h2 className="text-2xl font-bold text-gray-800 mb-4">
📚 Table of Contents
</h2>
<div className="grid md:grid-cols-2 gap-3">
<a
href="#basics"
className="text-green-600 hover:text-green-800 flex items-center"
>
<span className="bg-green-100 text-green-800 px-2 py-1 rounded mr-2 text-sm">
1
</span>
Node.js Basics & Fundamentals
</a>
<a
href="#modules"
className="text-green-600 hover:text-green-800 flex items-center"
>
<span className="bg-green-100 text-green-800 px-2 py-1 rounded mr-2 text-sm">
2
</span>
Modules & NPM
</a>
<a
href="#async"
className="text-green-600 hover:text-green-800 flex items-center"
>
<span className="bg-green-100 text-green-800 px-2 py-1 rounded mr-2 text-sm">
3
</span>
Asynchronous Programming
</a>
<a
href="#web"
className="text-green-600 hover:text-green-800 flex items-center"
>
<span className="bg-green-100 text-green-800 px-2 py-1 rounded mr-2 text-sm">
4
</span>
Web Development
</a>
<a
href="#database"
className="text-green-600 hover:text-green-800 flex items-center"
>
<span className="bg-green-100 text-green-800 px-2 py-1 rounded mr-2 text-sm">
5
</span>
Database Integration
</a>
<a
href="#advanced"
className="text-green-600 hover:text-green-800 flex items-center"
>
<span className="bg-green-100 text-green-800 px-2 py-1 rounded mr-2 text-sm">
6
</span>
Advanced Concepts
</a>
<a
href="#testing"
className="text-green-600 hover:text-green-800 flex items-center"
>
<span className="bg-green-100 text-green-800 px-2 py-1 rounded mr-2 text-sm">
7
</span>
Testing & Debugging
</a>
<a
href="#deployment"
className="text-green-600 hover:text-green-800 flex items-center"
>
<span className="bg-green-100 text-green-800 px-2 py-1 rounded mr-2 text-sm">
8
</span>
Deployment & DevOps
</a>
</div>
</nav>
{/* Introduction */}
<section className="bg-white rounded-lg shadow-md p-6 mb-8">
<h2 className="text-2xl lg:text-3xl font-bold text-gray-800 mb-4">
Understanding Node.js: The JavaScript Runtime
</h2>
<p className="text-gray-700 mb-4 leading-relaxed">
Node.js is an open-source, cross-platform JavaScript runtime environment that executes JavaScript code outside of a web browser.
Created by Ryan Dahl in 2009, Node.js enables developers to use JavaScript for server-side scripting and building scalable network applications.
</p>
<p className="text-gray-700 mb-4 leading-relaxed">
Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications
that run across distributed devices. It's built on Chrome's V8 JavaScript engine and uses libuv for handling asynchronous operations.
</p>
<div className="bg-yellow-50 border-l-4 border-yellow-500 p-4 my-4">
<p className="text-yellow-700 text-sm">
<strong>Did you know?</strong> Node.js was originally written in 2009 by Ryan Dahl, who was inspired to create it after seeing
a file upload progress bar on Flickr. He realized that the web needed a better way to handle I/O operations, leading to the
creation of the non-blocking, event-driven architecture that made Node.js revolutionary.
</p>
</div>
</section>
{/* Section 1: Basics */}
<section
id="basics"
className="bg-white rounded-lg shadow-md p-6 mb-8"
>
<h2 className="text-2xl lg:text-3xl font-bold text-gray-800 mb-6">
1. Node.js Basics & Fundamentals
</h2>
<div className="space-y-8">
<ArticleSection
title="Getting Started with Node.js"
content="Node.js can be installed on various operating systems and provides a powerful runtime for executing JavaScript on the server side. Understanding the basic concepts and setup is crucial."
code={\`// Basic Node.js script
// Save as app.js
console.log('Hello, Node.js!');
// Process information
console.log('Node.js version:', process.version);
console.log('Platform:', process.platform);
console.log('Current directory:', process.cwd());
// Command line arguments
console.log('Arguments:', process.argv);
// Simple function
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet('World'));
// Running the script:
// node app.js
// node app.js argument1 argument2
// File: calculator.js
// Simple calculator module
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
if (b === 0) {
throw new Error('Division by zero is not allowed');
}
return a / b;
}
// Export functions
module.exports = {
add,
subtract,
multiply,
divide
};
// Using the calculator in another file
// File: main.js
const calculator = require('./calculator');
console.log('5 + 3 =', calculator.add(5, 3));
console.log('10 - 4 =', calculator.subtract(10, 4));
console.log('6 * 7 =', calculator.multiply(6, 7));
console.log('15 / 3 =', calculator.divide(15, 3));
try {
console.log('10 / 0 =', calculator.divide(10, 0));
} catch (error) {
console.error('Error:', error.message);
}
Core Modules and Globals
Node.js comes with built-in modules that provide essential functionality. Understanding these core modules and global objects is fundamental to Node.js development.
// File: core-modules.js
// File System Module
const fs = require('fs');
const path = require('path');
// Path operations
console.log('Current file:', __filename);
console.log('Current directory:', __dirname);
console.log('Path separator:', path.sep);
console.log('File extension:', path.extname(__filename));
console.log('Base name:', path.basename(__filename));
console.log('Directory name:', path.dirname(__filename));
// File operations
const filePath = './example.txt';
// Write file
fs.writeFileSync(filePath, 'Hello, Node.js File System!\nThis is a second line.');
// Read file
const content = fs.readFileSync(filePath, 'utf8');
console.log('File content:', content);
// Append to file
fs.appendFileSync(filePath, '\nAppended content!');
// Check if file exists
if (fs.existsSync(filePath)) {
console.log('File exists');
}
// Read file asynchronously
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log('Async file content:', data);
});
// OS Module
const os = require('os');
console.log('OS Platform:', os.platform());
console.log('OS Architecture:', os.arch());
console.log('CPU Cores:', os.cpus().length);
console.log('Total Memory:', (os.totalmem() / 1024 / 1024 / 1024).toFixed(2), 'GB');
console.log('Free Memory:', (os.freemem() / 1024 / 1024 / 1024).toFixed(2), 'GB');
console.log('Home Directory:', os.homedir());
console.log('Uptime:', (os.uptime() / 60 / 60).toFixed(2), 'hours');
// URL Module
const url = require('url');
const exampleUrl = 'https://example.com:8080/path/name?query=string#hash';
const parsedUrl = url.parse(exampleUrl, true);
console.log('Protocol:', parsedUrl.protocol);
console.log('Host:', parsedUrl.host);
console.log('Path:', parsedUrl.pathname);
console.log('Query:', parsedUrl.query);
console.log('Hash:', parsedUrl.hash);
// Util Module
const util = require('util');
// Promisify callback-based functions
const readFileAsync = util.promisify(fs.readFile);
// Use with async/await
async function readFileExample() {
try {
const data = await readFileAsync(filePath, 'utf8');
console.log('Promisified file read:', data);
} catch (error) {
console.error('Error:', error);
}
}
readFileExample();
// Format strings
const formatted = util.format('Hello %s, you have %d messages', 'John', 5);
console.log('Formatted:', formatted);
// Events Module
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', (data) => {
console.log('Event received with data:', data);
});
myEmitter.on('error', (error) => {
console.error('Error occurred:', error);
});
// Emit events
myEmitter.emit('event', { message: 'Hello Event!' });
myEmitter.emit('error', new Error('Something went wrong'));
Working with File System
Node.js provides comprehensive file system operations through the fs module, supporting both synchronous and asynchronous file operations.
// File: file-operations.js
const fs = require('fs').promises; // Using promises API
const path = require('path');
class FileManager {
constructor(basePath = './data') {
this.basePath = basePath;
}
// Ensure directory exists
async ensureDirectory() {
try {
await fs.access(this.basePath);
} catch (error) {
await fs.mkdir(this.basePath, { recursive: true });
console.log('Directory created:', this.basePath);
}
}
// Create file with content
async createFile(filename, content) {
await this.ensureDirectory();
const filePath = path.join(this.basePath, filename);
await fs.writeFile(filePath, content);
console.log('File created:', filePath);
return filePath;
}
// Read file content
async readFile(filename) {
const filePath = path.join(this.basePath, filename);
try {
const content = await fs.readFile(filePath, 'utf8');
console.log('File read:', filePath);
return content;
} catch (error) {
throw new Error(`File not found: ${filePath}`);
}
}
// Update file (append)
async appendToFile(filename, content) {
const filePath = path.join(this.basePath, filename);
try {
await fs.appendFile(filePath, content);
console.log('Content appended to:', filePath);
} catch (error) {
throw new Error(`Cannot append to file: ${filePath}`);
}
}
// Delete file
async deleteFile(filename) {
const filePath = path.join(this.basePath, filename);
try {
await fs.unlink(filePath);
console.log('File deleted:', filePath);
} catch (error) {
throw new Error(`Cannot delete file: ${filePath}`);
}
}
// List files in directory
async listFiles() {
await this.ensureDirectory();
try {
const files = await fs.readdir(this.basePath);
console.log('Files in directory:', files);
return files;
} catch (error) {
throw new Error(`Cannot read directory: ${this.basePath}`);
}
}
// Get file information
async getFileInfo(filename) {
const filePath = path.join(this.basePath, filename);
try {
const stats = await fs.stat(filePath);
return {
filename,
size: stats.size,
created: stats.birthtime,
modified: stats.mtime,
isDirectory: stats.isDirectory(),
isFile: stats.isFile()
};
} catch (error) {
throw new Error(`Cannot get file info: ${filePath}`);
}
}
// Copy file
async copyFile(sourceFilename, destFilename) {
const sourcePath = path.join(this.basePath, sourceFilename);
const destPath = path.join(this.basePath, destFilename);
try {
await fs.copyFile(sourcePath, destPath);
console.log('File copied:', sourcePath, '->', destPath);
} catch (error) {
throw new Error(`Cannot copy file: ${sourcePath} to ${destPath}`);
}
}
// Watch file for changes
async watchFile(filename, callback) {
const filePath = path.join(this.basePath, filename);
try {
fs.watch(filePath, (eventType) => {
console.log(`File ${filename} changed: ${eventType}`);
callback(eventType, filename);
});
console.log('Watching file:', filePath);
} catch (error) {
throw new Error(`Cannot watch file: ${filePath}`);
}
}
}
// Usage example
async function demonstrateFileOperations() {
const fileManager = new FileManager();
try {
// Create and manipulate files
await fileManager.createFile('test.txt', 'Initial content\n');
await fileManager.appendToFile('test.txt', 'Additional content\n');
const content = await fileManager.readFile('test.txt');
console.log('Current content:', content);
const fileInfo = await fileManager.getFileInfo('test.txt');
console.log('File info:', fileInfo);
await fileManager.copyFile('test.txt', 'test-backup.txt');
await fileManager.listFiles();
// Watch for changes
await fileManager.watchFile('test.txt', (eventType, filename) => {
console.log(`Detected ${eventType} on ${filename}`);
});
// Simulate change after 2 seconds
setTimeout(async () => {
await fileManager.appendToFile('test.txt', 'Change from watcher\n');
}, 2000);
// Clean up after 5 seconds
setTimeout(async () => {
await fileManager.deleteFile('test.txt');
await fileManager.deleteFile('test-backup.txt');
}, 5000);
} catch (error) {
console.error('Error:', error.message);
}
}
// demonstrateFileOperations();
// Streams for large files
const { createReadStream, createWriteStream } = require('fs');
function copyLargeFile(source, destination) {
return new Promise((resolve, reject) => {
const readStream = createReadStream(source);
const writeStream = createWriteStream(destination);
readStream.on('error', reject);
writeStream.on('error', reject);
writeStream.on('finish', resolve);
readStream.pipe(writeStream);
});
}
// Example: Process large CSV file
async function processLargeFile() {
const readline = require('readline');
const fs = require('fs');
const fileStream = fs.createReadStream('./large-file.csv');
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
let lineCount = 0;
for await (const line of rl) {
lineCount++;
// Process each line
if (lineCount % 1000 === 0) {
console.log(`Processed ${lineCount} lines`);
}
}
console.log(`Total lines processed: ${lineCount}`);
}