Complete course on Angular foundations — Jim Cooper
Demo app: Joe’s Robot Shop (joes-robot-shop)
Table of Contents
- Module 1 — Getting Started with Angular
- Module 2 — Creating Standalone Angular Components
- Module 3 — Angular Template Syntax and Data Binding
- Module 4 — Styling Angular Components
- Module 5 — Angular Routing and Navigation
- Module 6 — Creating and Using Angular Services
- Overall Architecture of the Final Application
- 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
| Block | Role |
|---|---|
| Component | Controls a section of the user interface. Combines a TypeScript class, an HTML template, and CSS. |
| Template | HTML-like declarative syntax that connects application data to the interface. |
| Service | TypeScript class exposing reusable logic (data access, state management, business logic). Has no UI element. |
| Dependency Injection | Powerful system allowing components and services to inject their dependencies via the constructor. |
| Router | Maps 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
- Node.js — nodejs.org — used to run the development server and npm
- Angular CLI — installed globally via npm:
npm install -g @angular/cli
- Visual Studio Code — code.visualstudio.com
- 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:
| Question | Recommended Choice | Reason |
|---|---|---|
| Zoneless application? | Yes | Avoids zone.js, obsolete with Signals |
| CSS format | CSS | Simple, no additional dependencies |
| Server-Side Rendering | No | Not 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 stylespublic/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:
- Use the selector in the parent template:
<!-- app.html -->
<bot-catalog />
- 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
| Hook | Frequency | Primary Usage |
|---|---|---|
ngOnChanges | On each input change | React to data changes |
ngOnInit | Once only | Data fetching, initialization (most used) |
ngOnDestroy | Once only | Cleanup, 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
| Type | Syntax | Direction | Example |
|---|---|---|---|
| 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(5)] -->|"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
| Operation | Method | Example |
|---|---|---|
| Read value | Function call | availableInventory() |
| 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
trackclause 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]
| Type | File | Scope |
|---|---|---|
| Global | src/styles.css | Entire application |
| Component | *.component.css | Component 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
Linking Routes (routerLink)
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 { }
| Attribute | Usage |
|---|---|
href | Classic HTML navigation — full page reload |
routerLink | Angular 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() ?? []);
}
}
| Property | Description |
|---|---|
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
| Command | Description |
|---|---|
ng new <name> | Create a new Angular project |
ng serve | Start the development server |
ng g c <name> --type=component | Generate a component |
ng g s <name> --type=service | Generate a service |
ng build | Compile the application for production |
ng test | Run unit tests |
Angular Selectors and Template Syntax
| Syntax | Type | Example |
|---|---|---|
{{ 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) | Loop | Element repetition |
@if (condition) | Conditional | Conditional display |
routerLink="/path" | Navigation | Links between pages |
Key Angular Decorators and Functions
| Element | Usage |
|---|---|
@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