Beginner

Angular Foundations

Standalone components, template syntax and data binding, styling, routing and services.

Complete course on Angular foundations — Jim Cooper
Demo app: Joe’s Robot Shop (joes-robot-shop)


Table of Contents

  1. Module 1 — Getting Started with Angular
  2. Module 2 — Creating Standalone Angular Components
  3. Module 3 — Angular Template Syntax and Data Binding
  4. Module 4 — Styling Angular Components
  5. Module 5 — Angular Routing and Navigation
  6. Module 6 — Creating and Using Angular Services
  7. Overall Architecture of the Final Application
  8. Angular CLI Command Reference

Module 1 — Getting Started with Angular

What is Angular? Architectural Overview

Angular is a component-based framework for building modern web applications — dashboards, online stores, or enterprise applications — with a clean, maintainable architecture.

The Building Blocks of an Angular Application

BlockRole
ComponentControls a section of the user interface. Combines a TypeScript class, an HTML template, and CSS.
TemplateHTML-like declarative syntax that connects application data to the interface.
ServiceTypeScript class exposing reusable logic (data access, state management, business logic). Has no UI element.
Dependency InjectionPowerful system allowing components and services to inject their dependencies via the constructor.
RouterMaps URLs to components for page navigation.
graph TD
    A[Angular Application] --> B[AppComponent]
    B --> C[SiteHeaderComponent]
    B --> D[RouterOutlet]
    D --> E[CatalogComponent]
    D --> F[CartComponent]
    E --> G[ProductDetailsComponent]
    E --> H[ProductsService]
    F --> I[CartService]
    G --> I
    H -->|httpResource| J[Backend API /api/products]

    style A fill:#dd0031,color:#fff
    style B fill:#c3002f,color:#fff
    style H fill:#1976d2,color:#fff
    style I fill:#1976d2,color:#fff

Modern Angular Applications are Standalone

Standalone components declare their dependencies directly in their imports array, without requiring an NgModule.


Setting Up the Angular Development Environment

Prerequisites to Install

  1. Node.jsnodejs.org — used to run the development server and npm
  2. Angular CLI — installed globally via npm:
npm install -g @angular/cli
  1. Visual Studio Codecode.visualstudio.com
  2. Angular Extension in VS Code (search “Angular” in the extensions)

Creating and Exploring Our First Angular Application

ng new joes-robot-shop

The Angular CLI asks several questions during creation:

QuestionRecommended ChoiceReason
Zoneless application?YesAvoids zone.js, obsolete with Signals
CSS formatCSSSimple, no additional dependencies
Server-Side RenderingNoNot needed for this course

Generated Structure

joes-robot-shop/
├── src/
│   ├── index.html          ← HTML entry point
│   ├── main.ts             ← Application bootstrap
│   ├── styles.css          ← Global styles
│   └── app/
│       ├── app.ts          ← Root component
│       ├── app.html        ← Root component template
│       ├── app.css         ← Root component styles
│       ├── app.config.ts   ← Application configuration
│       └── app.routes.ts   ← Route definitions
├── public/                 ← Static files (images, etc.)
├── angular.json            ← Angular CLI configuration
└── package.json

Starting the Application

cd joes-robot-shop
ng serve

The application is accessible at http://localhost:4200


Cloning the Demo Application

# Rename the existing folder if necessary
mv joes-robot-shop joes-robot-shop-backup

# Clone the demo repo
git clone <REPO_URL> joes-robot-shop
cd joes-robot-shop

# Install npm dependencies
npm install

# Open in VS Code
code .

The demo repo includes:

  • styles.css: pre-configured global styles
  • public/images/: robot part images

Introduction to TypeScript

Angular is built with TypeScript, a superset of JavaScript that adds type safety.

Static Typing

// Classic JavaScript
let username = "Alice";
let score = 30;

// TypeScript with types
let username: string = "Alice";
let score: number = 30;
let isActive: boolean = true;

TypeScript Interfaces

// Define the shape of an object
interface IProduct {
  name: string;
  price: number;
  category?: string;  // Optional property with ?
}

// Usage
let gadget: IProduct = {
  name: "Speed Bot",
  price: 149
};

Access Modifiers

class AppComponent {
  public title: string = "Hello";    // Accessible anywhere (default)
  private _count: number = 0;        // Accessible only within the class
  protected data: string = "";       // Accessible in class and subclasses
}

Module 2 — Creating Standalone Angular Components

What is a Component?

An Angular component manages both the visual layout and behavior of a part of the user interface.

graph LR
    subgraph "Angular Component"
        A[TypeScript Class<br/>Logic & Data] --- B[HTML Template<br/>Visual Interface]
        B --- C[CSS File<br/>Visual Styles]
    end

Component Hierarchy (Joe’s Robot Shop example)

graph TD
    App[AppComponent<br/>app.ts] --> SH[SiteHeaderComponent<br/>site-header]
    App --> RO[RouterOutlet]
    RO --> Cat[CatalogComponent<br/>catalog]
    RO --> Cart[CartComponent<br/>cart]
    Cat --> PD["ProductDetailsComponent<br/>product-details (×N)"]
    Cart --> CI["CartItemComponent<br/>cart-item (×N)"]

    style App fill:#dd0031,color:#fff
    style Cat fill:#1565c0,color:#fff
    style Cart fill:#1565c0,color:#fff
    style PD fill:#0288d1,color:#fff
    style CI fill:#0288d1,color:#fff

Creating Our First Angular Component

With the Angular CLI

# Generate a component with type in the file name
ng generate component catalog --type=component

# Shortcut
ng g c catalog --type=component

Generated files:

src/app/catalog/
├── catalog.component.ts    ← Component class (logic)
├── catalog.component.html  ← HTML template
├── catalog.component.css   ← Component styles
└── catalog.component.spec.ts ← Unit tests

Component Structure

import { Component } from '@angular/core';

@Component({
  selector: 'bot-catalog',           // HTML selector to use this component
  imports: [],                        // Dependencies (other components, directives, pipes)
  templateUrl: './catalog.component.html',
  styleUrl: './catalog.component.css'
})
export class CatalogComponent {
  // Component logic and data
}

Displaying Our New Component

To use a component in another:

  1. Use the selector in the parent template:
<!-- app.html -->
<bot-catalog />
  1. Import the component in the parent class:
// app.ts
import { CatalogComponent } from './catalog/catalog.component';

@Component({
  selector: 'app-root',
  imports: [CatalogComponent],  // ← Required import
  templateUrl: './app.html',
  styleUrl: './app.css'
})
export class App { }

Defining the Component Selector Prefix

By default, Angular uses the app prefix. It is recommended to use a prefix unique to your application to:

  • Prevent naming conflicts with third-party libraries
  • Easily identify your components in the code

Changing the Prefix in angular.json

{
  "projects": {
    "joes-robot-shop": {
      "schematics": {},
      "prefix": "bot"  // ← Change "app" to your prefix
    }
  }
}

Future generated components will automatically use this prefix:

ng g c testing --type=component
# Generates: selector: 'bot-testing'

Accessing and Displaying Images

Images are stored in the public/ folder. During the build, this content is copied to the root of the site.

<!-- Direct access without mentioning "public/" in the URL -->
<img src="/images/robot-parts/head-friendly.png" alt="Friendly Bot" />

Creating Reusable Child Components

Instead of duplicating HTML, create a reusable child component:

ng g c product-details --type=component
// catalog.component.html
<ul>
  <li>
    <bot-product-details />
  </li>
  <li>
    <bot-product-details />
  </li>
</ul>
// catalog.component.ts
@Component({
  selector: 'bot-catalog',
  imports: [ProductDetailsComponent],  // ← Child component import
  templateUrl: './catalog.component.html',
})
export class CatalogComponent { }

Component Lifecycle Hooks

Each Angular component has a lifecycle defined by a series of events.

sequenceDiagram
    participant A as Angular
    participant C as Component

    A->>C: constructor()
    A->>C: ngOnChanges() ← if inputs change
    A->>C: ngOnInit() ← initialization (once only)
    loop On each input change
        A->>C: ngOnChanges()
        A->>C: ngDoCheck()
    end
    A->>C: ngOnDestroy() ← cleanup
HookFrequencyPrimary Usage
ngOnChangesOn each input changeReact to data changes
ngOnInitOnce onlyData fetching, initialization (most used)
ngOnDestroyOnce onlyCleanup, avoid memory leaks

Implementing a Lifecycle Hook

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

@Component({ ... })
export class CatalogComponent implements OnInit {

  ngOnInit(): void {
    // Executed once after component initialization
    // Ideal for loading data
    this.products = this.productsService.getProducts();
  }
}

Inline Templates and Styles

For small components, you can inline the template and styles directly in the decorator:

@Component({
  selector: 'bot-catalog',
  // Inline template with template string (backticks)
  template: `
    <ul>
      <li>Product 1</li>
    </ul>
  `,
  // Inline styles
  styles: `
    ul { list-style: none; }
  `
})
export class CatalogComponent { }

Note: For complex components, prefer separate files (.html / .css). Inline is reserved for very simple components.


Module 3 — Angular Template Syntax and Data Binding

Interpolation

Interpolation allows JavaScript expressions to be evaluated in HTML templates:

<!-- Double braces {{ }} for interpolation -->
<h1>2 + 2 = {{ 2 + 2 }}</h1>
<!-- Displayed result: 2 + 2 = 4 -->

<p>{{ product.name }}</p>
<p>{{ product.price * (1 - product.discount) }}</p>

Complex expressions (e.g. Math.round()) are not supported in interpolation. Complex logic must stay in the component class.


Data Binding with Interpolation

The Data Model: product.model.ts

export interface IProduct {
  id: number;
  description: string;
  name: string;
  imageName: string;
  category: string;
  price: number;
  discount: number;
}

Usage in a Component

// product-details.component.ts
import { IProduct } from '../product.model';

export class ProductDetailsComponent {
  product: IProduct;

  constructor() {
    this.product = {
      id: 1,
      name: "Speed Bot",
      description: "A fast and agile robot head",
      imageName: "head-speed.png",
      category: "Heads",
      price: 219.99,
      discount: 0.1
    };
  }
}
<!-- product-details.component.html -->
<h2>{{ product.name }}</h2>
<p>{{ product.description }}</p>
<p>{{ product.price | currency }}</p>

Attribute Bindings

Attribute bindings (brackets [ ]) bind an HTML attribute to an evaluated JavaScript expression:

<!-- Classic interpolation -->
<img src="/images/{{ product.imageName }}" alt="{{ product.name }}" />

<!-- Attribute binding — equivalent but more expressive -->
<img [src]="'/images/robot-parts/' + product.imageName" [alt]="product.name" />

Brackets [ ] tell Angular to evaluate the value as a JavaScript expression (not as plain text).

graph LR
    A["TypeScript Class<br/>product.name"] -->|One-way binding| B["HTML Template<br/>alt=Speed Bot"]
    style A fill:#1976d2,color:#fff
    style B fill:#388e3c,color:#fff

Calling Functions from a Template

For more complex logic, bind the attribute to a function’s return value:

// product-details.component.ts
getImageUrl(product: IProduct): string {
  return '/images/robot-parts/' + product.imageName;
}
<!-- product-details.component.html -->
<img [src]="getImageUrl(product)" [alt]="product.name" />

Responding to User Events (Event Binding)

Event bindings (parentheses ( )) bind DOM events to component methods:

<!-- Syntax: (eventName)="method()" -->
<button (click)="addToCart(product)">Buy</button>
<input (keyup)="onKeyUp($event)" />
<select (change)="onSelectionChange($event)">...</select>
// product-details.component.ts
addToCart(event: MouseEvent): void {
  console.log('Product added to cart', event);
  this.cartService.addToCart(this.product());
}
graph LR
    A["User Click<br/>Buy button"] -->|Event Binding| B["addToCart()"]
    B --> C[CartService.addToCart]
    C --> D[cart signal updated]
    D -->|Change Detection| E[UI updated]

    style A fill:#f57c00,color:#fff
    style D fill:#7b1fa2,color:#fff

Summary of Binding Types

TypeSyntaxDirectionExample
Interpolation{{ expr }}Class → Template{{ product.name }}
Attribute Binding[attr]="expr"Class → Template[src]="imageUrl"
Event Binding(event)="fn()"Template → Class(click)="buy()"
Two-way Binding[(ngModel)]="prop"Bidirectional[(ngModel)]="name"

Change Detection and Signals

Automatic Change Detection

Angular automatically detects changes in the following cases:

  • Mouse events (click, mousemove, etc.)
  • Keyboard events
  • Form events
  • Navigation / router events

The Problem with Asynchronous Changes

// ❌ Angular does not automatically detect this change
addToCart(): void {
  setTimeout(() => {
    this.availableInventory = 2;  // Asynchronous change not detected!
  }, 3000);
}

Solution: Angular Signals

A Signal is a reactive object that automatically notifies Angular when its value changes, even for asynchronous changes.

import { signal } from '@angular/core';

export class ProductDetailsComponent {
  // Create a signal initialized to 5
  availableInventory = signal(5);

  addToCart(event: MouseEvent): void {
    // Update the signal with .set() or .update()
    setTimeout(() => {
      this.availableInventory.update((current) => current - 1);
    }, 100);
  }
}
<!-- In the template, call the signal like a function -->
<p>Available inventory: {{ availableInventory() }}</p>
graph TD
    A[signal<br/>availableInventory = signal&lpar;5&rpar;] -->|"availableInventory()"| B[HTML Template<br/>Read the value]
    C[addToCart] -->|".set() or .update()"| A
    A -->|Automatic notification| D[Change Detection<br/>UI updated]

    style A fill:#7b1fa2,color:#fff
    style D fill:#388e3c,color:#fff
OperationMethodExample
Read valueFunction callavailableInventory()
Set a value.set()availableInventory.set(2)
Update.update()availableInventory.update(v => v - 1)

Passing Data to Child Components (Input Properties)

Input properties allow passing data from a parent component to a child component.

// product-details.component.ts
import { input } from '@angular/core';
import { IProduct } from '../product.model';

export class ProductDetailsComponent {
  // input.required() = required input
  product = input.required<IProduct>();
}
<!-- catalog.component.html — binding to the input property -->
<bot-product-details [product]="products()[0]" />
<bot-product-details [product]="products()[1]" />

Displaying Lists with @for

The @for control-flow repeats an HTML block for each element of an array:

<!-- catalog.component.html -->
<ul>
  @for (product of products(); track product.id) {
  <li>
    <bot-product-details [product]="product" />
  </li>
  }
</ul>

The track clause is required for performance reasons. It allows Angular to uniquely identify each element to re-render only the modified ones.

// catalog.component.ts
export class CatalogComponent {
  products!: Signal<IProduct[]>;

  constructor(private productsService: ProductsService) {}

  ngOnInit() {
    this.products = this.productsService.getProducts();
  }
}

Conditional Display with @if / @else

The @if control-flow conditionally displays content:

<!-- product-details.component.html -->
<div class="price">
  <!-- Original price — always displayed -->
  <p [ngClass]="getPriceClasses()">{{ product().price | currency }}</p>

  <!-- Discounted price — displayed only if discount > 0 -->
  @if (product().discount > 0) {
    <p>{{ product().price * (1 - product().discount) | currency }}</p>
  }

  <button class="cta" (click)="addToCart($event)">Buy</button>
</div>

With an @else Block

@if (product().discount > 0) {
  <p class="sale">{{ product().price * (1 - product().discount) | currency }} (Sale)</p>
} @else {
  <p>{{ product().price | currency }}</p>
}

Module 4 — Styling Angular Components

Global Styles vs Component Styles

graph TD
    subgraph "Global Styles (src/styles.css)"
        A[Styles applied<br/>to the entire application]
    end
    subgraph "Component Styles (*.component.css)"
        B[Styles scoped<br/>to the component only]
    end
    A --> C[Buttons, typography, base colors]
    B --> D[Layout, component-specific styles]
TypeFileScope
Globalsrc/styles.cssEntire application
Component*.component.cssComponent and descendants only

Component styles are encapsulated: they do not spill over to other components.

Declaring Multiple Global Style Files

In angular.json:

"styles": [
  "src/styles.css",
  "src/themes/dark-theme.css"
]

Conditional Class Bindings

Apply a CSS class conditionally with a class binding:

<!-- Syntax: [class.className]="condition" -->
<p [class.strikethrough]="product().discount > 0">
  {{ product().price | currency }}
</p>
/* product-details.component.css */
.strikethrough {
  text-decoration: line-through;
}

ngClass Directive

To apply multiple conditional classes simultaneously:

// product-details.component.ts
import { NgClass } from '@angular/common';

@Component({
  imports: [NgClass],  // ← Required import
  ...
})
export class ProductDetailsComponent {
  getPriceClasses() {
    return {
      strikethrough: this.product().discount > 0,
      'font-bold': this.product().price > 100,  // Class with dash: use a string
    };
  }
}
<!-- Binding to an object or a function returning an object -->
<p [ngClass]="getPriceClasses()">{{ product().price | currency }}</p>

Targeting the Host Element (host selector)

Each component generates a host element in the DOM (e.g. <bot-product-details>). Instead of wrapping the entire template in a <div> just for styling, use the :host selector:

/* product-details.component.css */
:host {
  display: flex;
  flex-direction: column;
  border: 1px solid #ccc;
  padding: 1rem;
}

:host img {
  max-width: 200px;
}
<!-- Before (with unnecessary div) -->
<div class="product">
  <img ... />
  <div>...</div>
</div>

<!-- After (with :host) -->
<img ... />
<div>...</div>

Module 5 — Angular Routing and Navigation

Preparing the Application for Routing

Routing allows mapping URLs to components. Angular CLI automatically creates app.routes.ts when generating a new project.

// app.config.ts — Application configuration
import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZonelessChangeDetection(),
    provideRouter(routes),
    provideHttpClient(),
  ]
};

Creating Routes

// app.routes.ts
import { Routes } from '@angular/router';
import { CatalogComponent } from './catalog/catalog.component';
import { CartComponent } from './cart/cart.component';

export const routes: Routes = [
  { path: '', redirectTo: '/catalog', pathMatch: 'full' },  // Root redirect
  { path: 'catalog', component: CatalogComponent },          // /catalog → CatalogComponent
  { path: 'cart', component: CartComponent },                // /cart → CartComponent
];

Indicating Where to Display Routed Components: <router-outlet>

<!-- app.html -->
<bot-site-header />
<router-outlet />  <!-- ← Routed components display here -->
// app.ts
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, SiteHeaderComponent],
  templateUrl: './app.html',
})
export class App { }
graph LR
    URL[Browser URL] --> Router[Angular Router]
    Router -->|/catalog| Cat[CatalogComponent]
    Router -->|/cart| Cart[CartComponent]
    Router -->|/| Redirect[Redirect to /catalog]

    Cat --> RO[RouterOutlet in app.html]
    Cart --> RO

    style Router fill:#dd0031,color:#fff
    style RO fill:#555,color:#fff

Instead of using href (which causes a page reload), Angular uses the routerLink directive:

<!-- site-header.component.html -->
<img src="/images/logo.png" alt="Logo" />
<a routerLink="/catalog">Catalog</a>
<a routerLink="/cart">Cart</a>
// site-header.component.ts
import { RouterLink } from '@angular/router';

@Component({
  selector: 'bot-site-header',
  imports: [RouterLink],  // ← Required import
  templateUrl: './site-header.component.html',
})
export class SiteHeaderComponent { }
AttributeUsage
hrefClassic HTML navigation — full page reload
routerLinkAngular navigation — SPA, only exchanges the displayed component

Module 6 — Creating and Using Angular Services

What is a Service?

An Angular service is a TypeScript class that encapsulates reusable logic. Unlike components, services have no UI element.

Why Use Services?

graph TD
    subgraph "Without services (❌ Anti-pattern)"
        C1[CatalogComponent<br/>fetch products]
        C2[CartComponent<br/>fetch products]
        C3[SearchComponent<br/>fetch products]
    end

    subgraph "With services (✅ Best practice)"
        PS[ProductsService<br/>fetch products]
        CC[CatalogComponent] --> PS
        CartC[CartComponent] --> PS
        SC[SearchComponent] --> PS
    end

Advantages of services:

  • Centralization of business logic
  • Avoids code duplication
  • State sharing between components (e.g. cart)
  • Facilitates unit testing

Creating an Angular Service

ng generate service products --type=service
# or shortcut
ng g s products --type=service

A generated service looks like this:

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

@Injectable({
  providedIn: 'root'  // ← Service available throughout the application (singleton)
})
export class ProductsService {
  // Service logic
}

The @Injectable({ providedIn: 'root' }) decorator declares the service as a singleton accessible anywhere in the application.


Injecting and Using a Service

Dependency injection is done via the constructor:

// catalog.component.ts
import { Component, OnInit, Signal } from '@angular/core';
import { ProductsService } from '../products.service';
import { IProduct } from '../product.model';

@Component({
  selector: 'bot-catalog',
  imports: [ProductDetailsComponent],
  templateUrl: './catalog.component.html',
})
export class CatalogComponent implements OnInit {
  products!: Signal<IProduct[]>;

  // Angular automatically injects ProductsService here
  constructor(private productsService: ProductsService) {}

  ngOnInit(): void {
    this.products = this.productsService.getProducts();
  }
}
graph TD
    DI[Dependency Injection<br/>Angular] -->|Creates and injects| PS[ProductsService]
    PS --> Cat[CatalogComponent<br/>constructor]
    PS --> Cart[CartComponent<br/>constructor]
    PS --> Other[Other components...]

    style DI fill:#dd0031,color:#fff
    style PS fill:#1976d2,color:#fff

Managing Application State with Services

Services are ideal for storing and sharing state between multiple components. The CartService uses a signal for reactivity:

// cart.service.ts
import { Injectable, signal } from '@angular/core';
import { IProduct } from './product.model';

@Injectable({
  providedIn: 'root'
})
export class CartService {
  // Signal to store the cart — reactive!
  cart = signal<IProduct[]>([]);

  addToCart(product: IProduct): void {
    this.cart.update(currentCart => [...currentCart, product]);
  }

  removeFromCart(product: IProduct): void {
    this.cart.update(currentCart =>
      currentCart.filter(p => p.id !== product.id)
    );
  }
}

Usage in product-details (add to cart)

// product-details.component.ts
import { CartService } from '../cart.service';

export class ProductDetailsComponent {
  product = input.required<IProduct>();
  availableInventory = signal(5);

  constructor(private cartService: CartService) {}

  addToCart(event: MouseEvent): void {
    setTimeout(() => this.availableInventory.update(p => p - 1), 100);
    this.cartService.addToCart(this.product());
  }
}
<!-- product-details.component.html -->
<div class="details">
  <img [src]="getImageUrl(product())" [alt]="product().name" />
  <div>
    <h2>{{ product().name }}</h2>
    <p>{{ product().description }}</p>
    <p>Part Type: {{ product().category }}</p>
    <p>Available inventory: {{ availableInventory() }}</p>
  </div>
</div>
<div class="price">
  <p [ngClass]="getPriceClasses()">{{ product().price | currency }}</p>
  @if (product().discount > 0) {
    <p>{{ product().price * (1 - product().discount) | currency }}</p>
  }
  <button class="cta" (click)="addToCart($event)">Buy</button>
</div>
sequenceDiagram
    participant U as User
    participant PD as ProductDetailsComponent
    participant CS as CartService (signal)
    participant CA as CartComponent

    U->>PD: Clicks "Buy"
    PD->>CS: addToCart(product)
    CS->>CS: cart.update([...cart, product])
    CS-->>CA: Signal notifies change
    CA->>CA: Displays new product in cart

Creating an API Proxy

When the backend API runs on a different port (e.g. 8081) and the Angular application on 4200, the browser blocks requests due to CORS (Cross-Origin Resource Sharing).

Solution: Development Proxy

Create a proxy.conf.json file:

{
  "/api": {
    "target": "http://localhost:8081",
    "secure": false
  }
}

Configure angular.json to use this proxy:

"serve": {
  "options": {
    "proxyConfig": "proxy.conf.json"
  }
}
graph LR
    A[Angular Application<br/>localhost:4200] -->|"/api/products"| B[Dev Proxy Server]
    B -->|Redirects to| C[Express.js API<br/>localhost:8081]
    C --> D[Returns products JSON]
    D --> B
    B --> A

Calling an API from a Service (httpResource)

Modern Angular uses httpResource to make reactive HTTP calls with signals.

Configure HTTP in app.config.ts

// app.config.ts
import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZonelessChangeDetection(),
    provideRouter(routes),
    provideHttpClient(),  // ← Required for HTTP calls
  ]
};

Service with httpResource

// products.service.ts
import { computed, Injectable } from '@angular/core';
import { httpResource, HttpResourceRef } from '@angular/common/http';
import { IProduct } from './product.model';

@Injectable({
  providedIn: 'root'
})
export class ProductsService {
  // httpResource makes the HTTP call and returns a reactive signal
  private resource: HttpResourceRef<IProduct[] | undefined> =
    httpResource(() => '/api/products');

  getProducts() {
    // computed() derives a value from a signal
    return computed(() => this.resource.value() ?? []);
  }
}
PropertyDescription
resource.value()The data returned by the API (signal)
resource.isLoading()true while loading
resource.error()The error if one occurred
resource.status()Request status

Overall Architecture of the Final Application

graph TB
    subgraph "Presentation Layer (Components)"
        App[AppComponent<br/>app.ts]
        SH[SiteHeaderComponent]
        Cat[CatalogComponent]
        PD[ProductDetailsComponent]
        Cart[CartComponent]
        CI[CartItemComponent]
    end

    subgraph "Business Logic Layer (Services)"
        PS[ProductsService<br/>httpResource]
        CS[CartService<br/>signal cart]
    end

    subgraph "Routing Layer"
        Router[Angular Router<br/>app.routes.ts]
    end

    subgraph "Backend"
        API[Express.js API<br/>:8081/api/products]
    end

    App --> SH
    App --> Router
    Router -->|/catalog| Cat
    Router -->|/cart| Cart
    Cat --> PD
    Cart --> CI
    Cat --> PS
    PD --> CS
    Cart --> CS
    PS -->|httpResource| API

    style App fill:#dd0031,color:#fff
    style PS fill:#1976d2,color:#fff
    style CS fill:#1976d2,color:#fff
    style Router fill:#7b1fa2,color:#fff
    style API fill:#388e3c,color:#fff

Complete Data Flow

flowchart TD
    A[API /api/products] -->|httpResource| B[ProductsService.resource]
    B -->|computed signal| C[CatalogComponent.products]
    C -->|"@for loop"| D["ProductDetailsComponent (×N)"]
    D -->|input.required| E[Product display]
    D -->|addToCart| F[CartService.cart signal]
    F -->|Signal propagation| G[CartComponent]
    G -->|"@for loop"| H["CartItemComponent (×N)"]

Angular CLI Command Reference

CommandDescription
ng new <name>Create a new Angular project
ng serveStart the development server
ng g c <name> --type=componentGenerate a component
ng g s <name> --type=serviceGenerate a service
ng buildCompile the application for production
ng testRun unit tests

Angular Selectors and Template Syntax

SyntaxTypeExample
{{ expr }}Interpolation{{ product.name }}
[attr]="expr"Property/Attribute Binding[src]="imageUrl"
(event)="fn()"Event Binding(click)="buy()"
[(ngModel)]="prop"Two-way Binding[(ngModel)]="email"
[class.x]="cond"Class Binding[class.active]="isActive"
[ngClass]="obj"Multi-class Binding[ngClass]="getClasses()"
@for (x of arr; track x.id)LoopElement repetition
@if (condition)ConditionalConditional display
routerLink="/path"NavigationLinks between pages

Key Angular Decorators and Functions

ElementUsage
@Component({ ... })Declares a component
@Injectable({ providedIn: 'root' })Declares a singleton service
signal(value)Creates a reactive signal
computed(() => ...)Derives a signal from other signals
input.required<T>()Declares a required input property
httpResource(() => url)Reactive HTTP call
provideRouter(routes)Configures the router
provideHttpClient()Enables HTTP calls

Search Terms

angular · foundations · frontend · development · component · application · components · data · service · binding · displaying · services · template · api · bindings · calling · change · child · cli · conditional · detection · display · functions · global

Interested in this course?

Contact us to book it or get a custom training plan for your team.