Angular : A Complete Guide

What is Angular?

Angular is a powerful TypeScript-based open-source framework developed by Google for building single-page applications (SPAs). It provides a rich set of features including components, services, dependency injection, routing, and more.

Setting Up Angular

Prerequisites

  1. Node.js and npm (Node Package Manager)
  2. Angular CLI

Installation

# Install Angular CLI globally
npm install -g @angular/cli

# Create a new Angular project
ng new my-angular-app

# Start the development server
ng serve

Core Concepts

1. Components

Components are the building blocks of Angular applications.

Component Structure

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <h1>{{ title }}</h1>
    <p>{{ description }}</p>
  `
})
export class AppComponent {
  title = 'My Angular App';
  description = 'Welcome to Angular!';
}

Creating a New Component

ng generate component my-component
# or
ng g c my-component

2. Data Binding

One-way Data Binding

// Component
export class DataBindingComponent {
  message = 'Hello Angular!';
}
<!-- Template -->
<h1>{{message}}</h1>

Two-way Data Binding

// Component
export class DataBindingComponent {
  name = '';
}
<!-- Template -->
<input [(ngModel)]="name">
<p>Hello, {{name}}!</p>

3. Directives

Built-in Directives

ngIf
<div *ngIf="isVisible">
  This content is conditionally displayed
</div>
ngFor
<ul>
  <li *ngFor="let item of items; let i = index">
    {{i + 1}}. {{item}}
  </li>
</ul>
ngSwitch
<div [ngSwitch]="color">
  <p *ngSwitchCase="'red'">Red</p>
  <p *ngSwitchCase="'blue'">Blue</p>
  <p *ngSwitchDefault>Default</p>
</div>

Custom Directive

// highlight.directive.ts
import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(el: ElementRef) {
    el.nativeElement.style.backgroundColor = 'yellow';
  }
}

4. Services and Dependency Injection

Creating a Service

// data.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  getData() {
    return ['Item 1', 'Item 2', 'Item 3'];
  }
}

Using the Service

// component.ts
import { Component } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data',
  template: `
    <ul>
      <li *ngFor="let item of items">{{item}}</li>
    </ul>
  `
})
export class DataComponent {
  items: string[];

  constructor(private dataService: DataService) {
    this.items = this.dataService.getData();
  }
}

5. Forms

Template-Driven Forms

// Component
export class UserFormComponent {
  user = {
    name: '',
    email: ''
  };

  onSubmit() {
    console.log(this.user);
  }
}
<!-- Template -->
<form #userForm="ngForm" (ngSubmit)="onSubmit()">
  <div>
    <label>Name:</label>
    <input [(ngModel)]="user.name" name="name" required>
  </div>
  <div>
    <label>Email:</label>
    <input [(ngModel)]="user.email" name="email" required email>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Submit</button>
</form>

Reactive Forms

// Component
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

export class ReactiveFormComponent {
  userForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.userForm = this.fb.group({
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]]
    });
  }

  onSubmit() {
    if (this.userForm.valid) {
      console.log(this.userForm.value);
    }
  }
}
<!-- Template -->
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
  <div>
    <label>Name:</label>
    <input formControlName="name">
  </div>
  <div>
    <label>Email:</label>
    <input formControlName="email">
  </div>
  <button type="submit" [disabled]="!userForm.valid">Submit</button>
</form>

6. HTTP Client

Making HTTP Requests

// service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ApiService {
  constructor(private http: HttpClient) {}

  getData() {
    return this.http.get('https://api.example.com/data');
  }

  postData(data: any) {
    return this.http.post('https://api.example.com/data', data);
  }
}

7. Routing

Basic Routing Setup

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: '**', redirectTo: '' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Router Navigation

<!-- Template -->
<nav>
  <a routerLink="/">Home</a>
  <a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet>

8. Lifecycle Hooks

import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-lifecycle',
  template: '<p>{{data}}</p>'
})
export class LifecycleComponent implements OnInit, OnDestroy {
  data: string = '';

  ngOnInit() {
    console.log('Component initialized');
    this.data = 'Initialized data';
  }

  ngOnDestroy() {
    console.log('Component destroyed');
  }
}

Best Practices

1. Project Structure

src/
  ├── app/
  │   ├── components/
  │   ├── services/
  │   ├── models/
  │   └── shared/
  ├── assets/
  └── environments/

2. Performance Tips

  1. Use OnPush change detection strategy
  2. Lazy load modules
  3. Use trackBy with ngFor
  4. Minimize subscription memory leaks

3. Error Handling

// Global error handler
import { ErrorHandler, Injectable } from '@angular/core';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  handleError(error: Error) {
    console.error('An error occurred:', error);
    // Log to server or show user-friendly message
  }
}

4. Testing

// Component test
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyComponent } from './my.component';

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ MyComponent ]
    })
    .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Example Application

Here’s a simple todo list application showcasing various Angular features:

// todo.component.ts
import { Component } from '@angular/core';

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

@Component({
  selector: 'app-todo',
  template: `
    <div class="todo-app">
      <h1>Todo List</h1>

      <form (ngSubmit)="addTodo()">
        <input [(ngModel)]="newTodoText" 
               name="newTodo" 
               placeholder="Add new todo">
        <button type="submit">Add</button>
      </form>

      <ul>
        <li *ngFor="let todo of todos">
          <input type="checkbox" 
                 [(ngModel)]="todo.completed">
          <span [class.completed]="todo.completed">
            {{todo.text}}
          </span>
          <button (click)="deleteTodo(todo.id)">Delete</button>
        </li>
      </ul>

      <div class="stats">
        Completed: {{completedCount}} / {{todos.length}}
      </div>
    </div>
  `,
  styles: [`
    .completed {
      text-decoration: line-through;
    }
  `]
})
export class TodoComponent {
  todos: Todo[] = [];
  newTodoText = '';

  get completedCount() {
    return this.todos.filter(todo => todo.completed).length;
  }

  addTodo() {
    if (this.newTodoText.trim()) {
      this.todos.push({
        id: Date.now(),
        text: this.newTodoText,
        completed: false
      });
      this.newTodoText = '';
    }
  }

  deleteTodo(id: number) {
    this.todos = this.todos.filter(todo => todo.id !== id);
  }
}

Deployment

To deploy your Angular application:

# Create production build
ng build --prod

# The built files will be in the dist/ folder
# Deploy these files to your web server

Remember to always check the official Angular documentation for the most up-to-date information and features.

Leave a Reply

Your email address will not be published. Required fields are marked *