### Start Development Server Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/README.md Start the development server to run the Angular Signals examples locally. ```bash npm start ``` -------------------------------- ### Clone Angular Signals Examples Repository Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/README.md Clone the repository to get started with the Angular Signals examples. ```bash git clone https://github.com/sonusindhu/angular-signals-examples.git cd angular-signals-examples ``` -------------------------------- ### Install Dependencies Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/README.md Install the necessary Node.js dependencies for the project. ```bash npm install ``` -------------------------------- ### Basic LinkedSignal Example Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/linked-signal/ls-example1/ls-example1.component.html.md This HTML template demonstrates a LinkedSignal setup where the quantity input automatically updates based on the selected course. It includes form controls for course selection and quantity input, along with display elements showing the linked signal values. ```html

Basic Resource Usage

Select Course Clear @for (course of courses(); track course.id) { {{ course.title }} } Quantity (Auto-updates based on course)

🔗 LinkedSignal in Action

Selected Course: {{getSelectedCourseTitle()}}

Default Quantity: {{getSelectedCourseDefaultQuantity()}}

Current Quantity: {{quantity()}}

💡 Notice how the quantity automatically updates when you change the course selection!

Key Features

  • Automatic Loading States: Built-in loading and error handling
  • Reactive Updates: Automatically refetches when parameters change
  • Type Safety: Full TypeScript support with proper typing
  • Simple API: Easy to use with minimal boilerplate
``` -------------------------------- ### Custom Preloading Strategy Example Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/advanced/example12/user-list/user-list.component.html A conceptual example of a custom preloading strategy that listens for a signal to preload a module. ```typescript import { Injectable } from '@angular/core'; import { PreloadingStrategy, Route } from '@angular/router'; import { Observable, Subject, switchMap, tap } from 'rxjs'; import { PreloadService } from './preload.service'; @Injectable({ providedIn: 'root' }) export class OnDemandPreloadingStrategy implements PreloadingStrategy { constructor(private preloadService: PreloadService) {} preload(route: Route, load: () => Observable): Observable { if (route.data && route.data['preloadOnDemand']) { return this.preloadService.preloadTrigger$.pipe( tap(path => console.log('Preload triggered for:', path)), switchMap(path => { if (path === route.path) { console.log('Loading module:', route.path); return load(); } else { return new Observable(); // Don't load if not the requested path } }) ); } return new Observable(); // Don't preload if not marked } } ``` -------------------------------- ### Component Integration to Trigger Preload Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/advanced/example12/user-list/user-list.component.html Example of injecting the preloading service and calling its trigger method on a mouse enter event. ```typescript import { Component, inject } from '@angular/core'; import { Router } from '@angular/router'; import { PreloadService } from './preload.service'; @Component({ selector: 'app-user-list', templateUrl: './user-list.component.html', standalone: true, imports: [/* ... */] }) export class UserListComponent { private router = inject(Router); private preloadService = inject(PreloadService); users = /* ... */ onUserRowHover(userId: number, userPath: string) { // Trigger preload for the user-form module when hovering over a user row this.preloadService.triggerPreload(userPath); } navigateToUserForm(userId: number) { this.router.navigate(['/user-form', userId]); } } ``` -------------------------------- ### Resource API Example 8 Component Styles Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/resource-api/resource-api-example8/resource-api-example8.component.scss.md SCSS styles for the Resource API Example 8 component. This includes layout for the main demo section, material card styling, action button alignment, country display elements, and visual cues for loading and error states. ```scss .demo-section { display: flex; flex-direction: column; align-items: center; mat-card { width: 100%; max-width: 500px; margin: 0 auto; } .demo-actions { display: flex; justify-content: center; margin-bottom: 16px; } .country-display { margin-top: 16px; .country-card { .country-flag { width: 60px; height: auto; margin-top: 8px; border: 1px solid #eee; border-radius: 4px; } .country-text { margin: 4px 0; } } } .loading-state, .error-state { display: flex; align-items: center; justify-content: center; color: #888; margin-top: 16px; mat-icon { margin-right: 8px; } } } ``` -------------------------------- ### Basic Defer Block Usage Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/defer-block/defer-block.component.html A simple example of the defer block to lazy load content. The content inside the defer block will only be rendered when it's needed. ```html @for (item of deferBlockExamples(); track item) { } ``` -------------------------------- ### Signal Initialization and Update Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/signal/signal-example2/signal-example2.component.ts.md Initializes a signal with a starting value and updates it periodically. This pattern is useful for managing simple state that changes over time. ```typescript import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ selector: 'app-signal-example2', imports: [CommonModule], templateUrl: './signal-example2.component.html', styleUrl: './signal-example2.component.scss', changeDetection: ChangeDetectionStrategy.OnPush }) export default class SignalExample2Component { count = signal(0); constructor() { setInterval(() => this.count.set(this.count() + 1), 1000); } } ``` -------------------------------- ### E-commerce Product Filters with Auto-Reset Pagination Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/linked-signal/ls-example5/ls-example5.component.html This example demonstrates how to use `linkedSignal` to auto-reset pagination when filters change, while still allowing manual page navigation. ```html Category All @for (cat of categories; track cat) { {{cat}} } Min Price Max Price @for(product of pagedProducts(); track product){ #### {{product.name}} ${{product.price}} Category: {{product.category}} } Prev Page {{page()}} of {{totalPages()}} Next ``` -------------------------------- ### Advanced Example 2 Component Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/advanced/advanced-example-2/advanced-example-2.component.ts.md Component class that injects a service to fetch user data. It uses RxJS observables to handle the asynchronous response. ```typescript import { Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AdvancedExample2Service } from './advanced-example-2.service'; import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; @Component({ selector: 'app-advanced-example-2', standalone: true, imports: [CommonModule, MatCardModule, MatButtonModule], templateUrl: './advanced-example-2.component.html', styleUrls: ['./advanced-example-2.component.scss'] }) export class AdvancedExample2Component { private service = inject(AdvancedExample2Service); users$ = this.service.getUsers(); } ``` -------------------------------- ### Router Configuration for On-Demand Preloading Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/advanced/example12/user-list/user-list.component.html Example of configuring Angular routes to use a custom preloading strategy and mark routes for on-demand preloading. ```typescript { path: 'user-form', loadChildren: () => import('./user-form/user-form.module').then(m => m.UserFormModule), data: { preloadOnDemand: true } } ``` -------------------------------- ### Defer Block with Prefetching Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/defer-block/defer-block.component.html Demonstrates prefetching strategies for defer blocks, such as 'prefetch on hover' and 'prefetch on interaction'. This helps to load content proactively, reducing perceived latency when the content is eventually needed. ```html
``` -------------------------------- ### HTTP GET Request with Caching Configuration Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/advanced/example1/example1.component.html This snippet shows how to make an HTTP GET request and configure caching using HttpContext and a custom CachingConfig token. Use this when you need to cache specific API responses for a defined duration. ```typescript const data$ = this.http.get('/sensitive/data', { context: new HttpContext().set(CACHING_CONFIG, { enabled: true, maxAge: 300000 // 5 minutes }), }); ``` -------------------------------- ### High Priority Defer Block (Immediate) Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/defer-block/defer-example11/defer-example11.component.html Loads critical business data immediately upon component initialization. Use for essential metrics that must be available without delay. ```html @defer (on immediate) { assessment Key Metrics Load immediately - critical business data ${{ salesData().revenue.toLocaleString() }} Revenue {{ salesData().growth }}% Growth {{ salesData().orders }} Orders priority_high Critical Data } @loading { Loading critical metrics... } ``` -------------------------------- ### Build for Production Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/README.md Build the Angular application for production deployment. ```bash npm run build ``` -------------------------------- ### Smart Loading Card with Multiple Triggers and Prefetch Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/defer-block/defer-example8/defer-example8-md.component.html This snippet uses multiple triggers (interaction, timer) and prefetching on hover. It's ideal for scenarios where content needs to be ready instantly upon user interaction after a hover, with a timer as a fallback. ```html Defer Block Example 8

🚀 Smart Loading Card

Hover to prefetch, click to load instantly

This card uses advanced defer strategies:

  • Prefetch on hover: Downloads resources when you hover
  • Load on interaction: Displays content when you click
  • Multiple fallbacks: Timer as backup trigger
@defer (on interaction(triggerElement); on timer(10s); prefetch on hover(triggerElement)) {

✨ Prefetched & Loaded Content

This content loaded instantly because it was prefetched!

Prefetch Benefits:

  • Resources downloaded on hover
  • Instant loading on interaction
  • Better user experience
  • Reduced perceived loading time

Multiple Trigger Strategy:

Primary: Interaction

Fallback: 10s Timer

Prefetch: Hover

Perfect for:

  • E-commerce product details
  • Modal dialogs and overlays
  • Media galleries
  • Interactive forms
  • Dashboard widgets
} @placeholder {

Hover over the card above to prefetch content, then click to load instantly...

} @loading (after 100ms; minimum 500ms) {

Loading prefetched content...

}
``` -------------------------------- ### E-commerce Prefetch Pattern Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/defer-block/defer-example8/defer-example8-md.component.html Demonstrates a common prefetch pattern for e-commerce scenarios, loading content on interaction and prefetching on hover. ```html @defer (on interaction; prefetch on hover) ``` -------------------------------- ### Untracked Signal Effect Example Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/signal/signal-example12/signal-example12.component.html This effect logs counter1 and an untracked read of counter2. Updating counter1 triggers the effect, but updating counter2 does not. ```typescript import { Component, effect, untracked, signal } from "@angular/core"; @Component({ selector: "app-signal-example12", standalone: true, imports: [], templateUrl: "./signal-example12.component.html", }) export class SignalExample12Component { counter1 = signal(1); counter2 = signal(1); constructor() { effect(() => { console.log(this.counter1(), untracked(() => this.counter2())); }); } updateCounter1() { this.counter1.update((c) => c + 1); } updateCounter2() { this.counter2.update((c) => c + 1); } } ``` -------------------------------- ### Defer Block with Basic Timing Control Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/defer-block/defer-example10/defer-example10.component.html Configures a defer block with 'after' and 'minimum' delays for both the placeholder and loading states. Use this to prevent UI flickering and ensure a smooth loading experience. ```html @defer (on interaction(basicTrigger)) {

✅ Content Loaded

Loaded with professional timing controls! } @placeholder (minimum 500ms) { 📋 Placeholder (min 500ms) Prevents immediate flash } @loading (after 200ms; minimum 1s) { ⏳ Loading (after 200ms, min 1s) Smooth loading transition } ``` -------------------------------- ### Defer Block with Basic Timing Control Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/defer-block/defer-example10/defer-example10.component.html This snippet shows a defer block configured with basic timing parameters for placeholder and loading states. It uses 'on interaction' to trigger loading and specifies minimum durations for both the placeholder and loading indicators to ensure a smooth transition. ```html @defer (on interaction(basicTrigger)) { #### ✅ Content Loaded Loaded with professional timing controls! } @placeholder (minimum 500ms) { 📋 Placeholder (min 500ms) Prevents immediate flash } @loading (after 200ms; minimum 1s) { ⏳ Loading (after 200ms, min 1s) Smooth loading transition } ``` -------------------------------- ### Linked Signal Component Styles Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/linked-signal/ls-example1/ls-example1.component.scss.md SCSS styles for the linked signal example component. These styles define the layout and appearance of various elements within the component. ```scss .container { max-width: 600px; margin: 0 auto; padding: 20px; } .form-control { margin-bottom: 20px; } .example-full-width { width: 100%; } .info-section { margin-top: 30px; } .info-section mat-card { background-color: #f5f5f5; border-left: 4px solid #673ab7; } .info-section h4 { color: #673ab7; margin-bottom: 15px; } .info-section p { margin: 8px 0; line-height: 1.6; } .note { font-style: italic; color: #666; background-color: #e8f5e8; padding: 10px; border-radius: 4px; border-left: 3px solid #4caf50; } .content-area { padding: 24px; } .demo-section { margin-top: 20px; } ``` -------------------------------- ### Defer Block: Immediate Loading Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/defer-block/defer-example11/defer-example11-md.component.html Loads content immediately upon rendering. Use for critical business data that must be available right away. ```html @defer (on immediate) { assessment Key Metrics Load immediately - critical business data ${{ salesData().revenue.toLocaleString() }} Revenue {{ salesData().growth }}% Growth {{ salesData().orders }} Orders } @loading { Loading critical metrics... } ``` -------------------------------- ### Manual HTTP Fetching vs. httpResource Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/cheatsheets/angular mistakes checklist.md Shows the difference between manually fetching HTTP data with `HttpClient` and subscriptions, versus using the `httpResource` utility for declarative HTTP data management. ```typescript this.http.get('/api/data').subscribe(...); ``` ```typescript data = httpResource(() => this.http.get('/api/data')); @if (data.loading()) { Loading... } @if (data.error()) { Error: {data.error()} } @if (data()) {
{data()}
} ``` -------------------------------- ### Switch Example 1 Component Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/control-flow/switch-section/switch-example-1/switch-example-1.component.ts.md This component manages a 'status' signal and updates it based on user input from a select element. It handles both direct string values and event targets. ```typescript import { Component, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatTabsModule } from '@angular/material/tabs'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; import { MarkdownComponent } from 'ngx-markdown'; @Component({ selector: 'app-switch-example-1', standalone: true, imports: [CommonModule, FormsModule, MatTabsModule, MatCardModule, MatIconModule, MarkdownComponent], templateUrl: './switch-example-1.component.html', styleUrls: ['./switch-example-1.component.scss'] }) export class SwitchExample1Component { status = signal('pending'); onStatusChange(eventOrValue: Event | string) { if (typeof eventOrValue === 'string') { this.status.set(eventOrValue); } else { const value = (eventOrValue.target instanceof HTMLSelectElement) ? eventOrValue.target.value : 'pending'; this.status.set(value); } } } ``` -------------------------------- ### Run Unit Tests Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/README.md Execute the unit tests for the Angular project. ```bash npm test ``` -------------------------------- ### Resource API Example 9 Component Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/resource-api/resource-api-example9/resource-api-example9.component.ts.md This component uses httpResource to fetch country data. The country selection is managed by an Angular signal, and the fetched data is displayed. Requires @angular/common/http. ```typescript import { Component, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; import { httpResource } from '@angular/common/http'; interface Country { name: { common: string }; capital?: string[]; region?: string; population?: number; flags?: { svg?: string }; } @Component({ selector: 'app-resource-api-example9', standalone: true, imports: [CommonModule, MatFormFieldModule, MatSelectModule, MatCardModule, MatIconModule], templateUrl: './resource-api-example9.component.html', styleUrls: ['./resource-api-example9.component.scss'] }) export class ResourceApiExample9Component { get countryData(): Country | null { const val = this.countryResource.value(); return Array.isArray(val) && val.length > 0 ? val[0] as Country : null; } countries = signal([ 'India', 'United States', 'Brazil', 'France', 'Japan', 'Australia', 'Canada', 'Germany', 'South Africa', 'China' ]); country = signal('India'); countryResource = httpResource(() => `https://restcountries.com/v3.1/name/${this.country()}?fullText=true` ); updateCountry(event: any) { this.country.set(event.value); } } ``` -------------------------------- ### Resource API Example 6 Component Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/resource-api/resource-api-example6/resource-api-example6.component.ts.md This component defines a signal 'jokeSignal' using httpResource to fetch a random joke from a specified URL. Ensure the necessary Angular modules and httpResource are imported. ```typescript import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; import { httpResource } from '@angular/common/http'; @Component({ selector: 'app-resource-api-example6', standalone: true, imports: [CommonModule, MatButtonModule, MatCardModule, MatIconModule], templateUrl: './resource-api-example6.component.html', styleUrl: './resource-api-example6.component.scss' }) export class ResourceApiExample6Component { jokeSignal = httpResource(() => "https://api.chucknorris.io/jokes/random"); } ``` -------------------------------- ### Resource API Usage Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/resource-api/resource-api-example4/resource-api-example4.component.ts.md This snippet demonstrates how to initialize a `resource` signal to fetch GitHub user data asynchronously. It uses a `loader` function to fetch data and provides a `defaultValue`. The `fetchGitHubUser` function handles the actual API call and error checking. ```typescript import { Component, inject, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { MatTabsModule } from '@angular/material/tabs'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; import { MarkdownComponent } from 'ngx-markdown'; import { resource } from '@angular/core'; interface GitHubUser { login: string; name: string; avatar_url: string; bio: string; followers: number; following: number; public_repos: number; html_url: string; } function getRandomUserId(): number { const userIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 583231, 872296, 1024025, 2225737, 3518823]; return userIds[Math.floor(Math.random() * userIds.length)]; } async function fetchGitHubUser(): Promise { const userId = getRandomUserId(); const response = await fetch(`https://api.github.com/user/${userId}`); if (!response.ok) { throw new Error('Failed to fetch user'); } return response.json(); } @Component({ selector: 'app-resource-api-example4', imports: [CommonModule, MatButtonModule, MatTabsModule, MatCardModule, MatIconModule, MarkdownComponent], templateUrl: './resource-api-example4.component.html', styleUrl: './resource-api-example4.component.scss' }) export class ResourceApiExample4Component { userSignal = resource({ loader: fetchGitHubUser, defaultValue: null as GitHubUser | null }); copyUsername(username: string) { navigator.clipboard.writeText(username); } } ``` -------------------------------- ### Defer Block with Loading and Error States Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/defer-block/defer-block.component.html Demonstrates handling loading and error states within a defer block. Use this to manage user expectations and provide alternative content or messages when loading fails. ```html
Loading content...

} loading {

Content is loading...

} error {

Failed to load content.

} ) {
``` -------------------------------- ### Computed Signal Example Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/signal/signal-example4/signal-example4.component.html This snippet shows a basic implementation of a computed signal in Angular. It derives a new signal's value based on changes in a base signal, automatically updating the UI. ```typescript import { Component, computed, signal } from "@angular/core"; @Component({ selector: "app-signal-example4", standalone: true, imports: [], templateUrl: "./signal-example4.component.html", }) export class SignalExample4Component { count = signal(0); doubleCount = computed(() => this.count() + 2); increment() { this.count.update((value) => value + 1); } } ``` -------------------------------- ### Defer Block: Viewport Loading with Prefetch Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/defer-block/defer-example11/defer-example11-md.component.html Loads content when it enters the viewport and prefetches on hover. Ideal for heavy components like charts that are not immediately critical. ```html @defer (on viewport(viewportWidget); prefetch on hover(viewportWidget)) { trending_up Analytics Chart Heavy chart component - loads when visible @for (bar of [65, 45, 80, 30, 90, 55, 75]; track $index) { {{ bar }}% } Performance metrics for the last 7 days } @placeholder { 📊 Scroll down to load analytics chart Chart will prefetch on hover } @loading (after 100ms; minimum 800ms) { Loading analytics data... Heavy chart component loading } ``` -------------------------------- ### HttpCacheService Implementation Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/advanced/example1/http-cache.service.ts.md This service uses a Map to store cached HTTP responses along with their expiration times. It includes methods for getting, setting, clearing cache entries, and an automatic cleanup mechanism. ```typescript import { Injectable } from "@angular/core"; import { HttpResponse } from "@angular/common/http"; import { timer } from "rxjs"; import { EXPIRE_CACHE_INTERVAL, HttpCacheEntry } from "./http-cache.model"; @Injectable({ providedIn: "root" }) export class HttpCacheService { private cache = new Map(); constructor() { // run cleanup immediately and every EXPIRE_CACHE_INTERVAL minutes timer(EXPIRE_CACHE_INTERVAL).subscribe(() => this.cleanup()); } get(key: string): HttpResponse | null { const entry = this.cache.get(key); if (!entry) return null; if (Date.now() > entry.expiry) { this.cache.delete(key); return null; } return entry.response.clone(); } set(key: string, response: HttpResponse, ttl: number): void { this.cache.set(key, { response: response.clone(), expiry: Date.now() + ttl }); } clear(key?: string): void { if (key) { this.cache.delete(key); } else { this.cache.clear(); } } private cleanup(): void { const now = Date.now(); for (const [key, entry] of this.cache.entries()) { if (entry.expiry <= now) { this.cache.delete(key); } } } } ``` -------------------------------- ### For Loop Example Component Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/control-flow/for-section/for-example-1/for-example-1.component.ts.md This component manages a count signal and provides a computed property 'items' that generates an array based on the current count. It also includes a method to update the count from user input. ```typescript import { Component, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatTabsModule } from '@angular/material/tabs'; import { MatCardModule } from '@angular/material/card'; import { MarkdownComponent } from 'ngx-markdown'; @Component({ selector: 'app-for-example-1', standalone: true, imports: [CommonModule, FormsModule, MatCardModule], templateUrl: './for-example-1.component.html', styleUrls: ['./for-example-1.component.scss'] }) export class ForExample1Component { count = signal(3); get items() { return Array(this.count()).fill(0); } onCountInput(event: Event) { const value = (event.target instanceof HTMLInputElement) ? event.target.valueAsNumber : 1; this.count.set(value || 1); } } ``` -------------------------------- ### Basic Resource Fetching and Display Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/resource-api/resource-api-example1/resource-api-example1.component.html This snippet shows how to set up and use a resource signal to fetch data, display loading and error states, and render the fetched data. It uses Angular's reactive features for dynamic updates. ```html refresh Refresh Data Current limit: {{ limit() }} items @if (resourceSignal.isLoading()) { cached Loading tasks... } @else if(resourceSignal.error()) { error Error: {{ resourceSignal.error() }} } @else { @let data = resourceSignal.value(); @if(data) { @for (r of data; track r.id) { } } ID Task Status {{ r.id }} {{ r.title }} {{ r.completed ? 'Completed' : 'Pending' }} } ``` -------------------------------- ### HTML Template for Resource API Example 6 Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/resource-api/resource-api-example6/resource-api-example6.component.html This HTML template displays a button to refresh the joke, conditionally renders the joke content, loading indicator, or error message based on the state of the jokeSignal. ```html @if (jokeSignal.value(); as joke) {

mood Chuck Norris Joke

{{$any(joke).value}}

ID: {{$any(joke).id}} link View Original

} @if (jokeSignal.isLoading()) {

hourglass_empty Loading a new joke...

} @if (jokeSignal.error()) {

error Error loading joke

} ``` -------------------------------- ### Defer Block with On Interaction Trigger Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/defer-block/defer-block.component.html Example of using the 'on interaction' trigger for a defer block, loading content only when the user interacts with a specific element. Useful for non-critical content that can be loaded upon user request. ```html
} }>
``` -------------------------------- ### Manual Price Calculation Without LinkedSignal Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/linked-signal/ls-example2/ls-example2.md Demonstrates a manual approach to price calculation using signals and effects, requiring explicit management of updates. This highlights the complexity compared to using linkedSignal. ```typescript // Manual calculation with effects export class ManualCalculator { basePrice = signal(100); quantity = signal(1); totalPrice = signal(0); constructor() { // Need to manually manage updates effect(() => { const subtotal = this.basePrice() * this.quantity(); this.totalPrice.set(subtotal * 1.18); }); } } ``` -------------------------------- ### Signal with Side Effect Example Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/signal/signal-example8/signal-example8.component.html This snippet demonstrates a basic Angular component using signals and the `effect()` function to log changes to a counter signal. It includes a button to update the counter and displays the current count in the template. ```typescript import { Component, effect, signal } from "@angular/core"; @Component({ selector: "app-signal-example8", standalone: true, imports: [], templateUrl: "./signal-example8.component.html", }) export class SignalExample8Component { count = signal(0); constructor() { effect(() => { console.log("The current count is: " + this.count()); }); } updateCount() { this.count.update((value) => value + 1); } } ``` -------------------------------- ### Host Styles for Demo Section Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/control-flow/for-section/for-example-1/for-example-1.component.scss.md Defines the base styles for the host element and the demo section, including padding, background, borders, margins, and box shadows. It also includes a transition for hover effects. ```scss :host { display: contents; .demo-section { padding: 1.5rem 1rem; background: #f8fafc; border: 1px solid #e0e0e0; border-radius: 10px; margin: 1.5rem 0; box-shadow: 0 2px 8px 0 rgba(60,60,60,0.06); transition: box-shadow 0.2s; } .demo-section:hover { box-shadow: 0 4px 16px 0 rgba(60,60,60,0.12); } .controls { margin-bottom: 1.2rem; display: flex; align-items: center; gap: 1rem; font-size: 1rem; } .controls input[type="number"] { width: 60px; margin-left: 0.5rem; padding: 0.2rem 0.4rem; border-radius: 4px; border: 1px solid #bdbdbd; font-size: 1rem; } .list-item { margin: 0.5rem 0; color: #388e3c; font-weight: 500; background: #e8f5e9; border-radius: 4px; padding: 0.4rem 0.8rem; box-shadow: 0 1px 2px 0 rgba(56,142,60,0.04); letter-spacing: 0.01em; } } ``` -------------------------------- ### Pricing Rules Engine Implementation Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/linked-signal/ls-example3/ls-example3.md Implement a private method to calculate prices based on product ID and user type, incorporating base prices and user-specific discounts. This function serves as the core business logic for pricing. ```typescript private getPriceFromRules(productId: string, userType: string): number { // Base pricing by product const basePrices = { 'p001': 1200, // Laptop 'p002': 800, // Phone 'p003': 600 // Tablet }; const basePrice = basePrices[productId] || 500; // User tier discounts const discounts = { 'regular': 1.0, // No discount 'premium': 0.9, // 10% discount 'enterprise': 0.8 // 20% discount }; return basePrice * (discounts[userType] || 1.0); } ``` -------------------------------- ### HTTP Cache Interceptor Implementation Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/advanced/example1/http-cache.interceptor.ts.md This interceptor caches GET requests. It checks for a cached response first and returns it if found. Otherwise, it proceeds with the network request, caches the response if it's an HttpResponse, and returns it. Caching can be configured per request using HttpContext. ```typescript import { HttpEvent, HttpHandlerFn, HttpRequest, HttpResponse } from "@angular/common/http"; import { inject } from "@angular/core"; import { Observable, of, tap } from "rxjs"; import { HttpCacheService } from "./http-cache.service"; import { CACHING_CONFIG, DEFAULT_CACHING_TTL } from "./http-cache.model"; export function httpCacheInterceptor( req: HttpRequest, next: HttpHandlerFn ): Observable> { const cacheService = inject(HttpCacheService); const config = req.context.get(CACHING_CONFIG); // Only cache GET requests if (req.method === "GET" && config?.enabled) { const cacheKey = config.key ?? req.urlWithParams; const cachedResponse = cacheService.get(cacheKey); if (cachedResponse) { console.log(`Cache hit for ${cacheKey}`); return of(cachedResponse); } return next(req).pipe( tap(event => { if (event instanceof HttpResponse) { const ttl = config.maxAge ?? DEFAULT_CACHING_TTL; cacheService.set(cacheKey, event, ttl); } }) ); } console.log(`fetching from network`); return next(req); } /** * Intercepts HTTP requests and responses to add caching behavior. * This interceptor checks if caching is enabled for the request, * retrieves cached responses if available, and stores new responses in the cache. * * Example usage: * * const data$ = this.http.get('/sensitive/data', { * context: new HttpContext().set(CACHING_CONFIG, { * enabled: true, * key: 'sensitive-data', * maxAge: 300000 // 5 minutes * }), * }); */ ``` -------------------------------- ### Iterating with Signals and TrackBy Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/ngs-table/ngs-table.component.html Demonstrates using `@for` loop with a signal for the iterable and a trackBy function. ```html @for (col of columnsConfig(); track col) { } @for (col of columnsConfig(); track col) { } ``` -------------------------------- ### Angular Component with rxResource Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/resource-api/resource-api-example8/resource-api-example8.component.ts.md This component uses rxResource to fetch country data reactively. It initializes signals for countries and the selected country, then uses rxResource to call an HTTP GET request when the country signal changes. The component also includes a method to update the selected country. ```typescript import { Component, signal, inject } from '@angular/core'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSelectChange, MatSelectModule } from '@angular/material/select'; import { MatCardModule } from '@angular/material/card'; import { MatTabsModule } from '@angular/material/tabs'; import { MarkdownComponent } from 'ngx-markdown'; import { CommonModule } from '@angular/common'; import { rxResource } from '@angular/core/rxjs-interop'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; import { MatIconModule } from '@angular/material/icon'; @Component({ selector: 'app-resource-api-example8', templateUrl: './resource-api-example8.component.html', styleUrls: ['./resource-api-example8.component.scss'], imports: [CommonModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatCardModule MatIconModule], }) export class ResourceApiExample8Component { countries = signal([ 'India', 'United States', 'Brazil', 'France', 'Japan', 'Australia', 'Canada', 'Germany', 'South Africa', 'China' ]); country = signal('India'); countryResource = rxResource((country: string) => this.http.get(`https://restcountries.com/v3.1/name/${country}?fullText=true`).pipe( map((res) => res[0]) ), { input: this.country } ); constructor(private http: HttpClient) {} updateCountry(event: any) { this.country.set(event.value); } } ``` -------------------------------- ### Linked Signal Example with Topics and Questions Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/linked-signal/ls-example8-ts.md This snippet defines a component that uses `linkedSignal` to manage the current question index, which is linked to the selected topic. It also uses `computed` signals to derive the list of questions for the current topic and the current question itself. This is useful for scenarios where multiple pieces of state are interdependent and need to be kept in sync. ```typescript import { Component, signal, linkedSignal, computed } from '@angular/core'; interface Question { text: string; } interface Topic { key: string; label: string; questions: Question[]; } const TOPICS: Topic[] = [ { key: 'math', label: 'Math', questions: [ { text: 'What is 2 + 2?' }, { text: 'What is the square root of 16?' }, { text: 'What is 10 divided by 2?' }, ], }, { key: 'science', label: 'Science', questions: [ { text: 'What planet is known as the Red Planet?' }, { text: 'What is H2O commonly known as?' }, { text: 'What force keeps us on the ground?' }, ], }, { key: 'history', label: 'History', questions: [ { text: 'Who was the first President of the United States?' }, { text: 'In which year did World War II end?' }, { text: 'What ancient civilization built the pyramids?' }, ], }, ]; @Component({ selector: 'app-ls-example8', // ... }) export class LsExample8Component { topics = TOPICS; topic = signal('math'); questions = computed(() => { const t = TOPICS.find(t => t.key === this.topic()); return t ? t.questions : []; }); currentIndex = linkedSignal({ source: () => this.topic(), computation: () => 0 }); currentQuestion = computed(() => this.questions()[this.currentIndex()]); setTopic(val: string) { this.topic.set(val); } prevQuestion() { if (this.currentIndex() > 0) this.currentIndex.set(this.currentIndex() - 1); } nextQuestion() { if (this.currentIndex() < this.questions().length - 1) this.currentIndex.set(this.currentIndex() + 1); } } ``` -------------------------------- ### Old: Manual Promise/Observable Handling Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/app/examples/cheatsheets/angular mistakes checklist.md Demonstrates the traditional method of manually handling asynchronous data fetching using Promises or Observables. ```typescript this.data = fetchData(); ``` -------------------------------- ### Defer Block: Timer Loading with Prefetch Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/defer-block/defer-example11/defer-example11-md.component.html Loads content automatically after a specified delay and prefetches immediately. Useful for content like news feeds that should appear after a short wait. ```html @defer (on timer(3s); prefetch on immediate) { article Latest News Auto-loads after 3 seconds @for (news of newsItems; track news.id) { ###### {{ news.title }} {{ news.summary }} {{ formatDate(news.date) }} } } @placeholder { 📰 News feed will load automatically in 3 seconds } @loading (minimum 500ms) { Loading latest news... } ``` -------------------------------- ### Defer Block with Placeholder Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/defer-block/defer-block.component.html Shows how to provide a placeholder element that is displayed while the deferred content is loading. This improves user experience by giving immediate feedback. ```html
Loading content...

}>
``` -------------------------------- ### Initialize and Manage Dynamic Form State Source: https://github.com/sonusindhu/angular-signals-examples/blob/master/src/assets/examples/signal-forms/example6/example6.component.ts.md Initialize the form state using `signal` and create the form instance with the defined schema. Component methods handle adding/removing projects and tasks, updating the signal state. ```typescript import { JsonPipe } from '@angular/common'; import { Component, signal, effect } from '@angular/core'; import { form, required, minLength, schema, applyEach, FormField, validateTree, ValidationError } from '@angular/forms/signals'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; // ... (type definitions and schema) @Component({ selector: 'app-example6', templateUrl: './example6.component.html', styleUrls: ['./example6.component.scss'], imports: [ MatButtonModule, MatTabsModule, Field, JsonPipe ], }) export class Example6Component { userProjects = signal({ username: '', projects: [], }); userProjectsForm = form(this.userProjects, userProjectsSchema); addProject() { this.userProjects.update((state) => ({ ...state, projects: [ ...state.projects, { name: '', deadline: '', status: 'Not Started', tasks: [], }, ], })); } removeProject(index: number) { this.userProjects.update((state) => ({ ...state, projects: state.projects.filter((_, i) => i !== index), })); } addTask(projectIndex: number) { this.userProjects.update((state) => { const projects = [...state.projects]; projects[projectIndex] = { ...projects[projectIndex], tasks: [ ...projects[projectIndex].tasks, { title: '', done: false, priority: 'Medium', dueDate: '', }, ], }; return { ...state, projects }; }); } removeTask(projectIndex: number, taskIndex: number) { this.userProjects.update((state) => { const projects = [...state.projects]; projects[projectIndex] = { ...projects[projectIndex], tasks: projects[projectIndex].tasks.filter((_, j) => j !== taskIndex), }; return { ...state, projects }; }); } getProjectTaskSummary(projectIndex: number): string { const project = this.userProjects().projects[projectIndex]; const total = project.tasks.length; const completed = project.tasks.filter(t => t.done).length; return `${completed} / ${total} tasks completed`; } public errors = effect(() => { return this.userProjectsForm().errors(); }); onSubmit() { if (this.userProjectsForm().valid()) { console.log('Submitted:', this.userProjects()); } } } ```