Angular Framework Overview

Module 25: Frontend Frameworks & State Management

Introduction to Angular

Angular is a comprehensive, opinionated framework for building single-page client applications using HTML, CSS, and TypeScript. It's maintained by Google and is used by many large enterprises for building robust web applications.

Angular History and Evolution

When people refer to "Angular" today, they're talking about Angular 2+ (the modern versions), not AngularJS.

Real-world analogy: If AngularJS (1.x) was like building a house with traditional construction methods, modern Angular is like building with a standardized, prefabricated system. While the end result might look similar, the modern approach provides better tools, more consistent patterns, and a more structured development process.

graph LR A[AngularJS 1.x] -->|Complete Rewrite| B[Angular 2] B -->|Semantic Versioning| C[Angular 4] C -->|Regular Updates| D[Angular...] D --> E[Angular 17+] style A fill:#dd0031 style B fill:#dd0031 style C fill:#dd0031 style D fill:#dd0031 style E fill:#dd0031 classDef default stroke:#333,stroke-width:2px;

Key Features of Angular

Angular provides a comprehensive solution with many built-in features:

TypeScript-First Approach

Angular is built with TypeScript and encourages its use, providing:

Component-Based Architecture

Angular applications are built from components:

Comprehensive Tooling

Angular CLI (Command Line Interface) provides:

Dependency Injection

A core feature that:

Reactive Programming Support

Angular integrates with RxJS:

Comprehensive Routing

Built-in router for single-page applications:

Form Handling

Two approaches to forms:

HTTP Client

Built-in service for communicating with APIs:

Core Angular Concepts

Modules

Angular organizes the application into cohesive blocks of functionality called NgModules:

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';
import { ProductListComponent } from './products/product-list.component';
import { ProductService } from './products/product.service';

@NgModule({
  imports: [     // Other modules whose exported classes are needed by component templates
    BrowserModule,
    FormsModule,
    HttpClientModule
  ],
  declarations: [ // Components, directives, and pipes that belong to this module
    AppComponent,
    ProductListComponent
  ],
  providers: [   // Services that will be available to the entire application
    ProductService
  ],
  bootstrap: [   // The main application view, called the root component
    AppComponent
  ]
})
export class AppModule { }

Components

Components are the building blocks of Angular applications:

// product-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Product } from './product.model';
import { ProductService } from './product.service';

@Component({
  selector: 'app-product-list',  // Used in HTML to represent this component
  templateUrl: './product-list.component.html', // The HTML template
  styleUrls: ['./product-list.component.css']  // The component-specific styles
})
export class ProductListComponent implements OnInit {
  products: Product[] = [];
  selectedProduct: Product | null = null;

  constructor(private productService: ProductService) { }

  ngOnInit(): void {
    this.getProducts();
  }

  getProducts(): void {
    this.productService.getProducts()
      .subscribe(products => this.products = products);
  }

  selectProduct(product: Product): void {
    this.selectedProduct = product;
  }
}

Templates

Templates define the component's view in HTML, enhanced with Angular's template syntax:

<!-- product-list.component.html -->
<div class="product-list">
  <h2>Products</h2>
  
  <div *ngIf="products.length === 0" class="no-data">
    No products found.
  </div>
  
  <ul *ngIf="products.length > 0">
    <li *ngFor="let product of products" 
        [class.selected]="product === selectedProduct"
        (click)="selectProduct(product)">
      <span class="name">{{product.name}}</span>
      <span class="price">{{product.price | currency}}</span>
    </li>
  </ul>
  
  <div *ngIf="selectedProduct" class="product-details">
    <h3>{{selectedProduct.name | uppercase}} Details</h3>
    <div>ID: {{selectedProduct.id}}</div>
    <div>
      <label for="product-name">Name: </label>
      <input id="product-name" [(ngModel)]="selectedProduct.name" placeholder="name">
    </div>
  </div>
</div>

Services

Services encapsulate non-UI logic and data shared across components:

// product.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, catchError, tap, of } from 'rxjs';
import { Product } from './product.model';

@Injectable({
  providedIn: 'root'  // Makes the service available throughout the application
})
export class ProductService {
  private productsUrl = 'api/products';  // URL to web API

  constructor(private http: HttpClient) { }

  getProducts(): Observable<Product[]> {
    return this.http.get<Product[]>(this.productsUrl)
      .pipe(
        tap(_ => console.log('fetched products')),
        catchError(this.handleError<Product[]>('getProducts', []))
      );
  }

  getProduct(id: number): Observable<Product> {
    const url = `${this.productsUrl}/${id}`;
    return this.http.get<Product>(url)
      .pipe(
        tap(_ => console.log(`fetched product id=${id}`)),
        catchError(this.handleError<Product>('getProduct'))
      );
  }

  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {
      console.error(`${operation} failed: ${error.message}`);
      // Let the app keep running by returning an empty result
      return of(result as T);
    };
  }
}
graph TD A[Module] --> B[Component] A --> C[Service] C --> B B --> D[Template] style A fill:#dd0031 style B fill:#dd0031 style C fill:#dd0031 style D fill:#dd0031 classDef default stroke:#333,stroke-width:2px;

Angular Architecture

The Angular architecture creates a clear separation of concerns while maintaining efficiency:

graph TD A[Angular Application] --> B[Modules] B --> C[Components] B --> D[Services] B --> E[Directives] B --> F[Pipes] C --> G[Templates] C --> H[Component Logic] C --> I[Component Styles] D --> J[Business Logic] D --> K[Data Access] D --> L[Shared Functionality] style A fill:#dd0031 style B fill:#dd0031 style C fill:#dd0031 style D fill:#dd0031 style E fill:#dd0031 style F fill:#dd0031 style G fill:#f99 style H fill:#f99 style I fill:#f99 style J fill:#f99 style K fill:#f99 style L fill:#f99 classDef default stroke:#333,stroke-width:2px;

Module Organization

A typical Angular application organizes modules as follows:

Component Hierarchy

Components form a tree structure, with data flowing down from parent to child:

Service Architecture

Services follow a layered approach:

Real-world analogy: Angular's architecture is like a well-designed city. Modules are like districts, each with a specific purpose. Components are like buildings, each with its own structure and function. Services are like utilities (water, electricity) that provide essential functionality across the city. The router is like the road system connecting everything together.

Data Flow and Binding

Angular offers several ways to connect component data to the template:

One-Way Data Binding

Event Binding

(event)="handler()" - Listens for DOM events and calls component methods in response

Two-Way Binding

[(ngModel)]="property" - Combines property binding and event binding for form controls

<!-- Different types of data binding -->

<!-- Interpolation -->
<h1>{{title}}</h1>
<p>Welcome, {{user.name}}!</p>

<!-- Property binding -->
<img [src]="product.imageUrl" [alt]="product.name">
<button [disabled]="isLoading">Submit</button>

<!-- Event binding -->
<button (click)="onSave()">Save</button>
<input (input)="onInput($event)">

<!-- Two-way binding (requires FormsModule) -->
<input [(ngModel)]="username">

<!-- Class and style binding -->
<div [class.active]="isActive" [style.color]="textColor">
  Dynamic styling
</div>
graph TB A[Component] -->|Interpolation {{ }} or Property Binding []| B[Template] B -->|Event Binding ()| A A -->|Two-way Binding [()]| B style A fill:#dd0031 style B fill:#dd0031 classDef default stroke:#333,stroke-width:2px;

Directives

Directives are classes that add behavior to elements in Angular templates. There are three types:

Components

Components are directives with a template (the most common type we've already discussed).

Structural Directives

These change the DOM layout by adding or removing elements:

<!-- ngIf example -->
<div *ngIf="user.isAdmin">Admin content here</div>

<!-- ngFor example -->
<ul>
  <li *ngFor="let item of items; let i = index; trackBy: trackByFn">
    {{i}}: {{item.name}}
  </li>
</ul>

<!-- ngSwitch example -->
<div [ngSwitch]="userRole">
  <div *ngSwitchCase="'admin'">Admin content</div>
  <div *ngSwitchCase="'editor'">Editor content</div>
  <div *ngSwitchDefault>User content</div>
</div>

Attribute Directives

These change the appearance or behavior of an existing element:

<!-- ngClass example -->
<div [ngClass]="{'active': isActive, 'disabled': isDisabled}">
  Dynamic classes
</div>

<!-- ngStyle example -->
<div [ngStyle]="{'color': textColor, 'font-size': fontSize + 'px'}">
  Dynamic styling
</div>

<!-- ngModel example -->
<input [(ngModel)]="username" placeholder="Username">

Custom Directives

You can create your own directives for specific behaviors:

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

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  @Input('appHighlight') highlightColor = '';
  @Input() defaultColor = 'yellow';

  constructor(private el: ElementRef) { }

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor || this.defaultColor);
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}

// Usage in template
<p appHighlight="lightblue" defaultColor="pink">
  Hover over me
</p>

Pipes

Pipes transform data for display in templates without modifying the original values:

Built-in Pipes

Chaining Pipes

Pipes can be chained for multiple transformations:

{{ birthday | date:'fullDate' | uppercase }}

Custom Pipes

You can create custom pipes for application-specific transformations:

// truncate.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'truncate'
})
export class TruncatePipe implements PipeTransform {
  transform(value: string, limit = 25, completeWords = false, ellipsis = '...') {
    if (!value) {
      return '';
    }
    
    if (value.length <= limit) {
      return value;
    }

    if (completeWords) {
      limit = value.substring(0, limit).lastIndexOf(' ');
    }
    
    return `${value.substring(0, limit)}${ellipsis}`;
  }
}

// Usage in template
<p>{{ longText | truncate:50:true }}</p>

Angular CLI

The Angular CLI is a command-line tool that makes Angular development easier:

Installation

npm install -g @angular/cli

Creating a New Project

ng new my-angular-app

Common Commands

Generating Code

// Generate a component
ng generate component products/product-list
// or the shorthand
ng g c products/product-list

// Generate a service
ng generate service products/product
// or the shorthand
ng g s products/product

// Generate a module
ng generate module products --routing
// or the shorthand
ng g m products --routing

// Generate other artifacts
ng g directive shared/highlight
ng g pipe shared/truncate
ng g class models/product
ng g interface models/product
ng g enum models/product-category

Each generation command creates the necessary files with boilerplate code, saving development time and ensuring consistent patterns.

Project Structure

A typical Angular project (created with the CLI) has the following structure:

my-angular-app/
├── src/                     # Source code of the application
│   ├── app/                 # Application components, templates, and logic
│   │   ├── app.component.ts         # Root component logic
│   │   ├── app.component.html       # Root component template
│   │   ├── app.component.css        # Root component styles
│   │   ├── app.component.spec.ts    # Root component tests
│   │   ├── app.module.ts            # Root module
│   │   ├── app-routing.module.ts    # Application routes
│   │   ├── core/                    # Core module for single-use components and services
│   │   ├── shared/                  # Shared module for commonly used components
│   │   └── features/                # Feature modules (products, orders, etc.)
│   ├── assets/              # Static assets like images, fonts, etc.
│   ├── environments/        # Environment-specific configuration
│   ├── index.html           # Main HTML file
│   ├── main.ts              # Entry point of the application
│   ├── polyfills.ts         # Browser polyfills
│   ├── styles.css           # Global styles
│   └── test.ts              # Testing configuration
├── node_modules/            # Dependencies
├── angular.json             # Angular CLI configuration
├── package.json             # NPM package configuration
├── tsconfig.json            # TypeScript configuration
├── karma.conf.js            # Karma test runner configuration
└── README.md                # Project documentation

This structure follows Angular best practices and provides a solid foundation for scaling applications.

Comparing Angular with Other Frameworks

Angular vs React

Feature Angular React
Type Full framework Library
Language TypeScript-first JavaScript with optional TypeScript
Learning Curve Steeper Moderate
Style Opinionated Flexible
DOM Manipulation Real DOM with change detection Virtual DOM
State Management Services, RxJS, NgRx React Context, Redux, MobX
Tooling Comprehensive CLI Create React App, but more options

Angular vs Vue

Feature Angular Vue
Type Full framework Progressive framework
Language TypeScript-first JavaScript with optional TypeScript
Learning Curve Steeper Gentle
Style Opinionated Flexible but with conventions
Component Structure Separate files for logic, template, styles Single-file components
Reactivity System Zone.js change detection Proxy-based reactivity
State Management Services, RxJS, NgRx Vuex, Pinia

When to Choose Angular

Angular is particularly well-suited for:

Real-world examples of companies using Angular:

Practice Activity

Setting Up Your First Angular Application

  1. Install the Angular CLI globally:
    npm install -g @angular/cli
  2. Create a new Angular application:
    ng new my-first-angular-app

    During setup, choose:

    • Add Angular routing? Yes
    • Which stylesheet format? CSS
  3. Navigate to the project directory:
    cd my-first-angular-app
  4. Start the development server:
    ng serve --open
  5. Generate your first component:
    ng generate component dashboard
  6. Modify the app.component.html file to include your dashboard component
  7. Create a simple dashboard with cards showing different metrics

Challenge Tasks

  1. Generate a service to provide data to your dashboard:
    ng generate service services/data
  2. Add methods to the service that return different types of data
  3. Inject the service into your dashboard component
  4. Use *ngFor to display multiple cards based on the data
  5. Add a button to each card that toggles detailed information
  6. Use pipes to format dates and numbers in your dashboard

Key Takeaways

Additional Resources