├── .env ├── package.json ├── tsconfig.json ├── src ├── shared │ ├── llamaindex │ │ ├── enableLogging.ts │ │ └── createCollectionFromMarkdownFiles.ts │ ├── file-utils │ │ ├── findMarkdownFiles.ts │ │ └── copySingleFile.ts │ ├── markdown │ │ ├── combineMarkdownFiles.ts │ │ ├── consolidateDirectory.ts │ │ ├── generateSingleDocFile.ts │ │ └── generateTableOfContents.ts │ └── github │ │ └── cloneRepository.ts ├── features │ └── angular-docs │ │ ├── specs.md │ │ └── generateAiFriendlyAngularDocs.ts └── angular.ts ├── .gitignore ├── ai-friendly-docs └── angular-19.2.3 │ └── sections │ ├── guide-performance.md │ ├── web-workers.md │ ├── custom-build-pipeline.md │ ├── prerendering.md │ ├── ssr.md │ ├── cli.md │ ├── rxjs-interop.md │ ├── guide-ngmodules.md │ ├── language-service.md │ ├── zoneless.md │ ├── elements.md │ ├── incremental-hydration.md │ ├── hydration.md │ ├── devtools.md │ ├── guide-pipes.md │ ├── hybrid-rendering.md │ └── guide-signals.md └── README.md /.env: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@llamaindex/huggingface": "^0.0.45", 4 | "@llamaindex/qdrant": "^0.1.8", 5 | "llamaindex": "^0.9.11", 6 | "tsx": "^4.19.3" 7 | }, 8 | "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c", 9 | "devDependencies": { 10 | "@types/node": "^22.13.5", 11 | "typescript": "^5.8.2" 12 | }, 13 | "scripts": { 14 | "generate:angular": "pnpm exec tsx --tsconfig=./tsconfig.json --env-file=./.env src/angular.ts", 15 | "tsc-noemit": "pnpm exec tsc --noEmit --project ./tsconfig.json --pretty true" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["ESNext"], 5 | "moduleDetection": "force", 6 | "module": "ESNext", 7 | "moduleResolution": "bundler", 8 | "resolveJsonModule": true, 9 | "allowJs": true, 10 | "esModuleInterop": true, 11 | "isolatedModules": true, 12 | "noErrorTruncation": true, 13 | "outDir": "./build", 14 | "rootDir": "./src", 15 | "strict": true, 16 | "skipLibCheck": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "declaration": true, 19 | "sourceMap": true, 20 | "paths": { 21 | "@shared/*": ["./src/shared/*"], 22 | "@features/*": ["./src/features/*"] 23 | } 24 | }, 25 | "include": ["src/**/*"], 26 | "exclude": ["node_modules", "build"] 27 | } 28 | -------------------------------------------------------------------------------- /src/shared/llamaindex/enableLogging.ts: -------------------------------------------------------------------------------- 1 | import { Settings } from "llamaindex"; 2 | 3 | export function enableLogging( 4 | events = [ 5 | "llm-start", 6 | "llm-end", 7 | "llm-tool-call", 8 | "llm-tool-result", 9 | "llm-stream", 10 | "chunking-start", 11 | "chunking-end", 12 | "node-parsing-start", 13 | "node-parsing-end", 14 | "query-start", 15 | "query-end", 16 | "synthesize-start", 17 | "synthesize-end", 18 | "retrieve-start", 19 | "retrieve-end", 20 | "agent-start", 21 | "agent-end", 22 | ] as const 23 | ) { 24 | // List of events: \node_modules\@llamaindex\core\global\dist\index.d.ts 25 | events.forEach((eventKey) => { 26 | Settings.callbackManager.on(eventKey, (event) => { 27 | console.log("---", event, event.detail); 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | 44 | *storybook.log 45 | 46 | /storybook-static 47 | /cloned-repos 48 | /scripts/node_modules 49 | -------------------------------------------------------------------------------- /src/shared/file-utils/findMarkdownFiles.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | 4 | /** 5 | * Recursively discovers all markdown files within a given directory. 6 | * 7 | * @param dirPath - The root directory to start searching for markdown files 8 | * @returns An array of absolute file paths for all discovered markdown files 9 | * 10 | * @description Traverses through directory structures to collect all markdown files, 11 | * ensuring comprehensive file discovery across nested directories. 12 | */ 13 | export function findMarkdownFiles(dirPath: string): string[] { 14 | const results: string[] = []; 15 | const items = fs.readdirSync(dirPath, { withFileTypes: true }); 16 | 17 | for (const item of items) { 18 | const fullPath = path.join(dirPath, item.name); 19 | 20 | if (item.isDirectory()) { 21 | // Recursively search subdirectories 22 | results.push(...findMarkdownFiles(fullPath)); 23 | } else if (item.name.endsWith(".md")) { 24 | results.push(fullPath); 25 | } 26 | } 27 | 28 | return results; 29 | } 30 | -------------------------------------------------------------------------------- /src/shared/markdown/combineMarkdownFiles.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | 4 | /** 5 | * Consolidates multiple markdown files into a single, structured document. 6 | * 7 | * @param files - An array of absolute file paths to markdown files 8 | * @returns A single markdown string combining all input files 9 | * 10 | * @description Merges markdown files with consistent formatting, 11 | * adjusts heading hierarchies, and adds file metadata to create 12 | * a comprehensive documentation compilation. 13 | */ 14 | export function combineMarkdownFiles(files: string[]): string { 15 | // Sort files to ensure consistent order 16 | files.sort(); 17 | 18 | let combinedContent = ""; 19 | 20 | for (const file of files) { 21 | const fileName = path.basename(file); 22 | let content = fs.readFileSync(file, "utf-8"); 23 | 24 | // Increase heading levels by one (# -> ##, ## -> ###, etc.) 25 | content = content.replace(/^(#{1,5}) /gm, "#$1 "); 26 | 27 | // Add file name as a heading, and include the content 28 | combinedContent += `\n\n(From ${fileName})\n\n`; 29 | combinedContent += content.trim(); 30 | combinedContent += "\n\n---\n"; 31 | } 32 | 33 | return combinedContent.trim(); 34 | } 35 | -------------------------------------------------------------------------------- /src/shared/file-utils/copySingleFile.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | 4 | /** 5 | * Parameters for copying a single markdown file 6 | */ 7 | export type CopySingleFileParams = { 8 | /** 9 | * The absolute path of the source markdown file 10 | */ 11 | sourcePath: string; 12 | 13 | /** 14 | * The desired filename in the destination directory 15 | */ 16 | targetFile: string; 17 | 18 | /** 19 | * The destination directory for the file 20 | */ 21 | sectionsDir: string; 22 | }; 23 | 24 | /** 25 | * Transfers a single markdown file to a designated sections directory. 26 | * 27 | * @param params - Parameters for file copying 28 | * 29 | * @description Copies a standalone markdown file to a centralized 30 | * documentation sections directory, preserving its original content. 31 | */ 32 | export function copySingleFile( 33 | params: CopySingleFileParams 34 | ): void { 35 | const { sourcePath, targetFile, sectionsDir } = params; 36 | 37 | if (!fs.existsSync(sourcePath)) { 38 | console.error(`Source file not found: ${sourcePath}`); 39 | return; 40 | } 41 | 42 | const content = fs.readFileSync(sourcePath, "utf-8"); 43 | const targetPath = path.join(sectionsDir, targetFile); 44 | 45 | fs.writeFileSync(targetPath, content); 46 | console.log(`Copied file: ${targetPath}`); 47 | } 48 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/guide-performance.md: -------------------------------------------------------------------------------- 1 | # Guide Performance 2 | 3 | (From overview.md) 4 | 5 | 6 | Learn about different ways you can optimize the performance of your application with different rendering strategies. 7 | 8 | 9 | One of the top priorities of any developer is ensuring that their application is as performant as possible. These guides are here to help you follow best practices for building performant applications by taking advantage of different rendering strategies. 10 | 11 | | Guides Types | Description | 12 | | :---------------------------------------- | :--------------------------------------------------------------------------------------------------------- | 13 | | [Server-side rendering](/guide/ssr) | Learn how to leverage rendering pages on the server to improve load times. | 14 | | [Build-time prerendering](/guide/prerendering) | Also known as static-site generation (SSG), is an alternate rendering method to improve load times. | 15 | | [Hydration](/guide/hydration) | A process to improve application performance by restoring its state after server-side rendering and reusing existing DOM structure as much as possible. | 16 | 17 | --- -------------------------------------------------------------------------------- /src/shared/llamaindex/createCollectionFromMarkdownFiles.ts: -------------------------------------------------------------------------------- 1 | import { QdrantVectorStore } from "@llamaindex/qdrant"; 2 | import fs from "fs"; 3 | import { 4 | Document, 5 | MarkdownNodeParser, 6 | VectorStoreIndex, 7 | type Metadata, 8 | type TextNode, 9 | } from "llamaindex"; 10 | import path from "path"; 11 | 12 | /** 13 | * Creates a collection from markdown files 14 | * @param collectionName - Name for the collection 15 | * @param files - Array of file paths to read and process 16 | * @returns The created vector index 17 | */ 18 | export async function createCollectionFromMarkdownFiles( 19 | collectionName: string, 20 | files: string[] 21 | ) { 22 | // Initialize parser and empty documents array 23 | const markdownNodeParser = new MarkdownNodeParser(); 24 | let allNodes: TextNode[] = []; 25 | 26 | // Process each file in the files array 27 | for (const filePath of files) { 28 | try { 29 | const text = fs.readFileSync(filePath, "utf-8"); 30 | const fileName = path.basename(filePath); 31 | 32 | // Create document with metadata 33 | const document = new Document({ 34 | text, 35 | metadata: { 36 | originalFile: fileName, 37 | }, 38 | }); 39 | 40 | // Parse document into nodes and add to collection 41 | const nodes = markdownNodeParser([document]); 42 | allNodes = [...allNodes, ...nodes]; 43 | } catch (error) { 44 | console.error(`Error processing file ${filePath}:`, error); 45 | } 46 | } 47 | 48 | // Initialize vector store with the collection name 49 | const vectorStore = new QdrantVectorStore({ 50 | url: "http://localhost:6333", 51 | collectionName, 52 | }); 53 | 54 | // Create and return the index from all collected nodes 55 | const index = await VectorStoreIndex.fromDocuments(allNodes, { 56 | vectorStores: { TEXT: vectorStore }, 57 | }); 58 | 59 | return index; 60 | } 61 | -------------------------------------------------------------------------------- /src/shared/markdown/consolidateDirectory.ts: -------------------------------------------------------------------------------- 1 | import { findMarkdownFiles } from "@shared/file-utils/findMarkdownFiles"; 2 | import * as fs from "fs"; 3 | import * as path from "path"; 4 | import { combineMarkdownFiles } from "./combineMarkdownFiles"; 5 | 6 | /** 7 | * Parameters for consolidating a directory of markdown files 8 | */ 9 | export type ConsolidateDirectoryParams = { 10 | /** 11 | * The directory containing source markdown files 12 | */ 13 | sourceDir: string; 14 | 15 | /** 16 | * The name of the output markdown file 17 | */ 18 | targetFile: string; 19 | 20 | /** 21 | * The destination directory for consolidated files 22 | */ 23 | sectionsDir: string; 24 | }; 25 | 26 | /** 27 | * Generates a consolidated markdown file from a source documentation directory. 28 | * 29 | * @param params - Parameters for directory consolidation 30 | * 31 | * @description Processes markdown files in a source directory, 32 | * combines them into a single comprehensive document, 33 | * and saves the result in a specified sections directory. 34 | */ 35 | export function consolidateDirectory(params: ConsolidateDirectoryParams): void { 36 | const { sourceDir, targetFile, sectionsDir } = params; 37 | 38 | if (!fs.existsSync(sourceDir)) { 39 | console.error(`Source directory not found: ${sourceDir}`); 40 | return; 41 | } 42 | 43 | const files = findMarkdownFiles(sourceDir); 44 | 45 | if (files.length === 0) { 46 | console.warn(`No markdown files found in directory: ${sourceDir}`); 47 | return; 48 | } 49 | 50 | // Create title from the target file name (capitalized, without dashes) 51 | const title = path 52 | .basename(targetFile, ".md") 53 | .split("-") 54 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) 55 | .join(" "); 56 | 57 | const combinedContent = `# ${title}\n\n` + combineMarkdownFiles(files); 58 | const targetPath = path.join(sectionsDir, targetFile); 59 | 60 | fs.writeFileSync(targetPath, combinedContent); 61 | console.log( 62 | `Created consolidated file: ${targetPath} from ${files.length} markdown files` 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /src/features/angular-docs/specs.md: -------------------------------------------------------------------------------- 1 | # AI-Friendly Angular Documentation Generator 2 | 3 | ## Feature Type 4 | 5 | Data Transformation Feature (following the Data Transformation Blueprint) 6 | 7 | ## Purpose and Scope 8 | 9 | This feature is responsible for generating AI-friendly Angular documentation by transforming and organizing content from multiple source files into a structured set of markdown files. It processes various sections of Angular documentation according to specific rules for different documentation types to optimize content for AI consumption. 10 | 11 | ## Requirements 12 | 13 | ### Functional Requirements 14 | 15 | - Consolidate various sections of Angular documentation into structured markdown files 16 | - Create individual section files for different documentation areas 17 | - Generate a single comprehensive file containing all documentation 18 | - Generate a table of contents file for easy navigation 19 | - Support different consolidation strategies based on documentation section type 20 | 21 | ### Non-functional Requirements 22 | 23 | - Process a large number of markdown files efficiently 24 | - Maintain heading hierarchy in consolidated documents 25 | - Preserve content structure from original documentation 26 | - Provide clear logging for the consolidation process 27 | 28 | ## Technical Design 29 | 30 | The feature follows a modular design with specialized functions for different aspects of the consolidation process: 31 | 32 | - Directory and file finding utilities to locate source documentation 33 | - Consolidation utilities that combine multiple files into a single document 34 | - File processing utilities for transforming content 35 | - Table of contents generation 36 | - Individual section processors for different types of Angular documentation 37 | 38 | The consolidation process: 39 | 1. Cleans and creates the target directory structure 40 | 2. Processes each documentation section based on its type 41 | 3. Generates a combined file with all documentation 42 | 4. Creates a table of contents file 43 | 44 | ## Dependencies 45 | 46 | This feature depends on: 47 | - Node.js file system module for file operations 48 | - Path module for path manipulation 49 | 50 | ## Testing Strategy 51 | 52 | - Unit tests for individual transformation functions 53 | - Integration tests for the complete consolidation process 54 | - Validation tests to ensure output files are properly formatted 55 | - Edge case tests for handling missing files or directories 56 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/web-workers.md: -------------------------------------------------------------------------------- 1 | # Background processing using web workers 2 | 3 | [Web workers](https://developer.mozilla.org/docs/Web/API/Web_Workers_API) let you run CPU-intensive computations in a background thread, freeing the main thread to update the user interface. 4 | Application's performing a lot of computations, like generating Computer-Aided Design \(CAD\) drawings or doing heavy geometric calculations, can use web workers to increase performance. 5 | 6 | HELPFUL: The Angular CLI does not support running itself in a web worker. 7 | 8 | ## Adding a web worker 9 | 10 | To add a web worker to an existing project, use the Angular CLI `ng generate` command. 11 | 12 | 13 | 14 | ng generate web-worker 15 | 16 | 17 | 18 | You can add a web worker anywhere in your application. 19 | For example, to add a web worker to the root component, `src/app/app.component.ts`, run the following command. 20 | 21 | 22 | 23 | ng generate web-worker app 24 | 25 | 26 | 27 | The command performs the following actions. 28 | 29 | 1. Configures your project to use web workers, if it isn't already. 30 | 1. Adds the following scaffold code to `src/app/app.worker.ts` to receive messages. 31 | 32 | 33 | 34 | addEventListener('message', ({ data }) => { 35 | const response = `worker response to ${data}`; 36 | postMessage(response); 37 | }); 38 | 39 | 40 | 41 | 1. Adds the following scaffold code to `src/app/app.component.ts` to use the worker. 42 | 43 | 44 | 45 | if (typeof Worker !== 'undefined') { 46 | // Create a new 47 | const worker = new Worker(new URL('./app.worker', import.meta.url)); 48 | worker.onmessage = ({ data }) => { 49 | console.log(`page got message: ${data}`); 50 | }; 51 | worker.postMessage('hello'); 52 | } else { 53 | // Web workers are not supported in this environment. 54 | // You should add a fallback so that your program still executes correctly. 55 | } 56 | 57 | 58 | 59 | After you create this initial scaffold, you must refactor your code to use the web worker by sending messages to and from the worker. 60 | 61 | IMPORTANT: Some environments or platforms, such as `@angular/platform-server` used in [Server-side Rendering](guide/ssr), don't support web workers. 62 | 63 | To ensure that your application works in these environments, you must provide a fallback mechanism to perform the computations that the worker would otherwise perform. 64 | -------------------------------------------------------------------------------- /src/shared/github/cloneRepository.ts: -------------------------------------------------------------------------------- 1 | import { exec } from "child_process"; 2 | import { promisify } from "util"; 3 | import fs from "fs"; 4 | import path from "path"; 5 | 6 | /** 7 | * Parameters for cloning a GitHub repository 8 | */ 9 | export type CloneRepositoryParams = { 10 | /** Repository owner/organization name */ 11 | owner: string; 12 | /** Repository name */ 13 | repo: string; 14 | /** Version, tag, or branch to clone */ 15 | version: string; 16 | /** Directory where the repository will be cloned */ 17 | targetDir: string; 18 | /** Clone depth (use 1 for shallow clone, omit for full clone) */ 19 | depth?: number; 20 | }; 21 | 22 | /** 23 | * Clones a GitHub repository with the specified parameters. 24 | * If the target directory already exists, skips cloning. 25 | * 26 | * @param params - The parameters for cloning the repository 27 | * @returns Promise that resolves when the cloning process completes 28 | * @throws Error if cloning fails 29 | * 30 | * @example 31 | * ```typescript 32 | * await cloneRepository({ 33 | * owner: "angular", 34 | * repo: "angular", 35 | * version: "19.2.2", 36 | * targetDir: "./cloned-repos/angular-19.2.2", 37 | * depth: 1 38 | * }); 39 | * ``` 40 | */ 41 | export async function cloneRepository(params: CloneRepositoryParams): Promise { 42 | const { owner, repo, version, targetDir, depth } = params; 43 | const execAsync = promisify(exec); 44 | 45 | // Create parent directory if it doesn't exist 46 | if (!fs.existsSync(path.dirname(targetDir))) { 47 | fs.mkdirSync(path.dirname(targetDir), { recursive: true }); 48 | } 49 | 50 | // Skip cloning if target directory already exists 51 | if (fs.existsSync(targetDir)) { 52 | console.log(`Target directory already exists, skipping clone: ${targetDir}`); 53 | return; 54 | } 55 | 56 | // Build git clone command 57 | let cloneCommand = `git clone`; 58 | 59 | // Add depth parameter if specified 60 | if (depth !== undefined) { 61 | cloneCommand += ` --depth ${depth}`; 62 | } 63 | 64 | // Add branch/tag parameter if version is specified 65 | if (version) { 66 | cloneCommand += ` --branch ${version}`; 67 | } 68 | 69 | // Add repository URL and target directory 70 | cloneCommand += ` https://github.com/${owner}/${repo}.git ${targetDir}`; 71 | 72 | // Clone the repository 73 | console.log(`Cloning ${owner}/${repo} repository (version ${version})...`); 74 | try { 75 | await execAsync(cloneCommand); 76 | console.log(`Successfully cloned ${owner}/${repo} repository to ${targetDir}`); 77 | } catch (error) { 78 | console.error(`Failed to clone ${owner}/${repo} repository: ${error}`); 79 | throw error; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/shared/markdown/generateSingleDocFile.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | 4 | /** 5 | * Parameters for generating a single documentation file 6 | */ 7 | export type GenerateSingleDocFileParams = { 8 | /** 9 | * Array of markdown files to be consolidated 10 | */ 11 | sectionFiles: string[]; 12 | 13 | /** 14 | * Directory containing the section markdown files 15 | */ 16 | sectionsDir: string; 17 | 18 | /** 19 | * Destination path for the comprehensive documentation file 20 | */ 21 | outputPath: string; 22 | }; 23 | 24 | /** 25 | * Creates a comprehensive single documentation file from multiple section files. 26 | * 27 | * @param params - Parameters for single doc file generation 28 | * 29 | * @description Aggregates multiple documentation sections into a unified, 30 | * hierarchically structured markdown document, providing a complete overview 31 | * of the project's documentation. 32 | */ 33 | export function generateSingleDocFile( 34 | params: GenerateSingleDocFileParams 35 | ): void { 36 | const { sectionFiles, sectionsDir, outputPath } = params; 37 | console.log("Generating single comprehensive Angular documentation file..."); 38 | 39 | // Make sure parent directory exists 40 | const parentDir = path.dirname(outputPath); 41 | if (!fs.existsSync(parentDir)) { 42 | fs.mkdirSync(parentDir, { recursive: true }); 43 | } 44 | 45 | // Create title and intro 46 | let content = "# Angular Complete Documentation\n\n"; 47 | content += 48 | "This is a comprehensive collection of Angular documentation consolidated into a single file for easier reference.\n\n"; 49 | 50 | // Loop through all section files and append their content 51 | for (const sectionFile of sectionFiles) { 52 | console.log( 53 | `Processing ${sectionFile} for inclusion in single doc file...` 54 | ); 55 | const sectionContent = fs.readFileSync( 56 | path.join(sectionsDir, sectionFile), 57 | "utf-8" 58 | ); 59 | 60 | // Get the first line that should contain the title 61 | const titleMatch = sectionContent.match(/^# (.+)$/m); 62 | const sectionTitle = titleMatch 63 | ? titleMatch[1] 64 | : path.basename(sectionFile, ".md"); 65 | 66 | // Add section divider and title as H2 67 | content += "\n\n---\n\n"; 68 | content += `## ${sectionTitle}\n\n`; 69 | 70 | // Add the rest of the content, removing the first title line 71 | let cleanedContent = sectionContent.replace(/^# .+$/m, ""); 72 | 73 | // Increase heading level for all headings in the content (# -> ##, ## -> ###, etc.) 74 | cleanedContent = cleanedContent.replace(/^(#{1,5}) /gm, "#$1 "); 75 | 76 | content += cleanedContent.trim(); 77 | } 78 | 79 | // Write the single file 80 | fs.writeFileSync(outputPath, content); 81 | console.log(`Created single documentation file: ${outputPath}`); 82 | } 83 | -------------------------------------------------------------------------------- /src/angular.ts: -------------------------------------------------------------------------------- 1 | import { generateAiFriendlyAngularDocs } from "@features/angular-docs/generateAiFriendlyAngularDocs"; 2 | import { HuggingFaceEmbedding } from "@llamaindex/huggingface"; 3 | import { findMarkdownFiles } from "@shared/file-utils/findMarkdownFiles"; 4 | import { cloneRepository } from "@shared/github/cloneRepository"; 5 | import { createCollectionFromMarkdownFiles } from "@shared/llamaindex/createCollectionFromMarkdownFiles"; 6 | import { Settings } from "llamaindex"; 7 | 8 | /** 9 | * Main entry point for the AI-friendly Angular documentation generation script. 10 | * Creates documentation and vector embeddings for Angular framework. 11 | * 12 | * @remarks 13 | * This script performs two main operations: 14 | * 1. Generates AI-friendly documentation from Angular source files 15 | * 2. Creates vector embeddings and stores them in a Qdrant collection 16 | * 17 | * Uses default configuration settings defined in the feature. 18 | * 19 | * @returns Promise that resolves when the process completes 20 | * @throws Error if documentation generation or embedding creation fails 21 | * @see generateAiFriendlyAngularDocs for the main transformation logic 22 | * @see createCollectionFromMarkdownFiles for the embedding process 23 | */ 24 | async function main() { 25 | /** Angular version to process */ 26 | const version = "19.2.3"; 27 | /** Repository directory */ 28 | const repoDir = `./cloned-repos/angular-${version}`; 29 | /** Source directory containing original Angular documentation */ 30 | const sourceDir = `${repoDir}/adev/src/content`; 31 | /** Target directory where AI-friendly documentation will be written */ 32 | const targetDir = `./ai-friendly-docs/angular-${version}`; 33 | 34 | try { 35 | // Clone the Angular repository with the specified version 36 | await cloneRepository({ 37 | owner: "angular", 38 | repo: "angular", 39 | version: version, 40 | targetDir: repoDir, 41 | depth: 1, 42 | }); 43 | await generateAiFriendlyAngularDocs({ 44 | sourceDir, 45 | targetDir, 46 | }); 47 | console.log( 48 | "AI-friendly Angular documentation generation completed successfully!" 49 | ); 50 | 51 | // Configure embedding model for vector generation 52 | Settings.embedModel = new HuggingFaceEmbedding({ 53 | modelType: "Xenova/all-MiniLM-L6-v2", 54 | }); 55 | 56 | // Create vector embeddings and store in Qdrant collection 57 | await createCollectionFromMarkdownFiles( 58 | `angular-${version}`, 59 | findMarkdownFiles(`./ai-friendly-docs/angular-${version}/sections`) 60 | ); 61 | console.log( 62 | `AI-friendly Angular documentation Qdrant collection (angular-${version}) generation completed successfully!` 63 | ); 64 | process.exit(0); 65 | } catch (error) { 66 | console.error( 67 | "Error during AI-friendly Angular documentation generation:", 68 | error 69 | ); 70 | process.exit(1); 71 | } 72 | } 73 | 74 | // Execute the documentation generation process 75 | // This is the entry point for the script 76 | main(); 77 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/custom-build-pipeline.md: -------------------------------------------------------------------------------- 1 | # Custom build pipeline 2 | 3 | When building an Angular app we strongly recommend you to use the Angular CLI to leverage its structure-dependent update functionality and build system abstraction. This way your projects benefit from the latest security, performance, and API improvements and transparent build improvements. 4 | 5 | This page explores the **rare use cases** when you need a custom build pipeline that does not use the Angular CLI. All listed tools below are open source build plugins that are maintained by members of the Angular community. To learn more about their support model and maintenance status look at their documentation and GitHub repository URLs. 6 | 7 | ## When should you use a custom build pipeline? 8 | 9 | There are some niche use cases when you may want to maintain a custom build pipeline. For example: 10 | 11 | * You have an existing app using a different toolchain and you’d like to add Angular to it 12 | * You’re strongly coupled to [module federation](https://module-federation.io/) and unable to adopt bundler-agnostic [native federation](https://www.npmjs.com/package/@angular-architects/native-federation) 13 | * You’d like to create an short-lived experiment using your favorite build tool 14 | 15 | ## What are the options? 16 | 17 | Currently, there are two well supported community tools that enable you to create a custom build pipeline with a [Vite plugin](https://www.npmjs.com/package/@analogjs/vite-plugin-angular) and [Rspack plugin](https://www.npmjs.com/package/@ng-rspack/build). Both of them use underlying abstractions that power the Angular CLI. They allow you to create a flexible build pipeline and require manual maintenance and no automated update experience. 18 | 19 | ### Rspack 20 | 21 | Rspack is a Rust-based bundler that aims to provide compatibility with the webpack plugin ecosystem. 22 | 23 | If your project is tightly coupled to the webpack ecosystem, heavily relying on a custom webpack configuration you can leverage Rspack to improve your build times. 24 | 25 | You can find more about Angular Rspack on the project’s [documentation website](https://angular-rspack.dev/guide/migration/from-webpack). 26 | 27 | ### Vite 28 | 29 | Vite is a frontend build tool that aims to provide a faster and leaner development experience for modern web projects. Vite is also extensible through its plugin system that allows ecosystems to build integrations with Vite, such as Vitest for unit and browser testing, Storybook for authoring components in isolation, and more. The Angular CLI also uses Vite as its development server. 30 | 31 | The [AnalogJS Vite plugin for Angular](https://www.npmjs.com/package/@analogjs/vite-plugin-angular) enables the adoption of Angular with a project or framework that uses or is built on top of Vite. This can consist of developing and building an Angular project with Vite directly, or adding Angular to an existing project or pipeline. One example is integrating Angular UI components into a documentation website using [Astro and Starlight](https://analogjs.org/docs/packages/astro-angular/overview). 32 | 33 | You can learn more about AnalogJS and how to use the plugin through its [documentation page](https://analogjs.org/docs/packages/vite-plugin-angular/overview). 34 | -------------------------------------------------------------------------------- /src/shared/markdown/generateTableOfContents.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | 4 | /** 5 | * Parameters for generating a table of contents 6 | */ 7 | export type GenerateTableOfContentsParams = { 8 | /** 9 | * Array of markdown files representing documentation sections 10 | */ 11 | sectionFiles: string[]; 12 | 13 | /** 14 | * Directory containing the section markdown files 15 | */ 16 | sectionsDir: string; 17 | 18 | /** 19 | * Path to the comprehensive documentation file 20 | */ 21 | mainDocPath: string; 22 | 23 | /** 24 | * Destination path for the table of contents file 25 | */ 26 | outputPath: string; 27 | }; 28 | 29 | /** 30 | * Generates a structured table of contents for the consolidated documentation. 31 | * 32 | * @param params - Parameters for table of contents generation 33 | * 34 | * @description Creates a navigable index of documentation sections and subsections, 35 | * providing a clear overview and easy navigation for the consolidated documentation. 36 | */ 37 | export function generateTableOfContents( 38 | params: GenerateTableOfContentsParams 39 | ): void { 40 | const { sectionFiles, sectionsDir, mainDocPath, outputPath } = params; 41 | console.log("Generating table of contents for Angular documentation..."); 42 | 43 | // Make sure parent directory exists 44 | const parentDir = path.dirname(outputPath); 45 | if (!fs.existsSync(parentDir)) { 46 | fs.mkdirSync(parentDir, { recursive: true }); 47 | } 48 | 49 | // Start with a title 50 | let tocContent = "# Angular Documentation - Table of Contents\n\n"; 51 | 52 | // Add main entry for the comprehensive file 53 | tocContent += `- [Complete Angular Documentation](${path.basename( 54 | mainDocPath 55 | )})\n`; 56 | 57 | // Add section for individual files 58 | tocContent += "\n## Documentation Sections\n\n"; 59 | 60 | // Process each section file to extract its title and headings 61 | for (const sectionFile of sectionFiles) { 62 | const filePath = path.join(sectionsDir, sectionFile); 63 | if (!fs.existsSync(filePath)) { 64 | console.warn(`Section file not found: ${filePath}`); 65 | continue; 66 | } 67 | 68 | const sectionContent = fs.readFileSync(filePath, "utf-8"); 69 | 70 | // Get the first heading as the section title 71 | const titleMatch = sectionContent.match(/^# (.+)$/m); 72 | const sectionTitle = titleMatch 73 | ? titleMatch[1] 74 | : path.basename(sectionFile, ".md"); 75 | 76 | // Add section entry 77 | tocContent += `- [${sectionTitle}](sections/${sectionFile})\n`; 78 | 79 | // Extract all h2 headings (originally h1 in the source files) 80 | const h2Headings = sectionContent.match(/^## .+$/gm); 81 | 82 | if (h2Headings && h2Headings.length > 0) { 83 | // Add indented sub-entries 84 | h2Headings.forEach((heading) => { 85 | const headingText = heading.replace(/^## /, ""); 86 | // If heading contains 'From ', it's a file marker rather than a real heading 87 | if (!headingText.startsWith("From ")) { 88 | const headingSlug = headingText 89 | .toLowerCase() 90 | .replace(/[^\w\s-]/g, "") 91 | .replace(/[\s]+/g, "-"); 92 | tocContent += ` - [${headingText}](sections/${sectionFile}#${headingSlug})\n`; 93 | } 94 | }); 95 | } 96 | } 97 | 98 | // Write the TOC file 99 | fs.writeFileSync(outputPath, tocContent); 100 | console.log(`Created table of contents file: ${outputPath}`); 101 | } 102 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/prerendering.md: -------------------------------------------------------------------------------- 1 | # Prerendering (SSG) 2 | 3 | Prerendering, commonly referred to as Static Site Generation (SSG), represents the method by which pages are rendered to static HTML files during the build process. 4 | 5 | Prerendering maintains the same performance benefits of [server-side rendering (SSR)](guide/ssr#why-use-server-side-rendering) but achieves a reduced Time to First Byte (TTFB), ultimately enhancing user experience. The key distinction lies in its approach that pages are served as static content, and there is no request-based rendering. 6 | 7 | When the data necessary for server-side rendering remains consistent across all users, the strategy of prerendering emerges as a valuable alternative. Rather than dynamically rendering pages for each user request, prerendering takes a proactive approach by rendering them in advance. 8 | 9 | ## How to prerender a page 10 | 11 | To prerender a static page, add SSR capabilities to your application with the following Angular CLI command: 12 | 13 | 14 | 15 | ng add @angular/ssr 16 | 17 | 18 | 19 |
20 | 21 | To create an application with prerendering capabilities from the beginning use the [`ng new --ssr`](tools/cli/setup-local) command. 22 | 23 |
24 | 25 | Once SSR is added, you can generate the static pages by running the build command: 26 | 27 | 28 | 29 | ng build 30 | 31 | 32 | 33 | ### Build options for prerender 34 | 35 | The application builder `prerender` option can be either a Boolean or an Object for more fine-tuned configuration. 36 | When the option is `false`, no prerendering is done. When it is `true`, all options use the default value. When it is an Object, each option can be individually configured. 37 | 38 | | Options | Details | Default Value | 39 | | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------ | 40 | | `discoverRoutes` | Whether the builder should process the Angular Router configuration to find all unparameterized routes and prerender them. | `true` | 41 | | `routesFile` | The path to a file that contains a list of all routes to prerender, separated by newlines. This option is useful if you want to prerender routes with parameterized URLs. | | 42 | 43 | 44 | 45 | { 46 | "projects": { 47 | "my-app": { 48 | "architect": { 49 | "build": { 50 | "builder": "@angular-devkit/build-angular:application", 51 | "options": { 52 | "prerender": { 53 | "discoverRoutes": false 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | 63 | 64 | ### Prerendering parameterized routes 65 | 66 | You can prerender parameterized routes using the `routesFile` option. An example of a parameterized route is `product/:id`, where `id` is dynamically provided. To specify these routes, they should be listed in a text file, with each route on a separate line. 67 | 68 | For an app with a large number of parameterized routes, consider generating this file using a script before running `ng build`. 69 | 70 | 71 | 72 | /products/1 73 | /products/555 74 | 75 | 76 | 77 | With routes specified in the `routes.txt` file, use the `routesFile` option to configure the builder to prerender the product routes. 78 | 79 | 80 | 81 | { 82 | "projects": { 83 | "my-app": { 84 | "architect": { 85 | "build": { 86 | "builder": "@angular-devkit/build-angular:application", 87 | "options": { 88 | "prerender": { 89 | "routesFile": "routes.txt" 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | 99 | 100 | This configures `ng build` to prerender `/products/1` and `/products/555` at build time. 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AI-Friendly Documentation Project 2 | 3 | This repository contains AI-optimized documentation for various open source projects. The goal is to provide documentation in a format that is easily consumable by Large Language Models (LLMs). 4 | 5 | #### Using the Generated Documentation 6 | 7 | The AI-friendly documentation can be used in several ways: 8 | 9 | 1. **Direct Consumption by LLMs**: The markdown files can be directly fed to LLMs to improve their understanding of Angular concepts 10 | 11 | 2. **RAG Integration**: The vector embeddings can be used in RAG systems to enhance LLM responses with accurate Angular documentation 12 | 13 | 3. **Training Data**: The processed documentation can serve as high-quality training data for future AI models 14 | 15 | For more insights on improving LLM responses with AI-friendly documentation, see my article: ["Getting Better LLM Responses Using AI-Friendly Documentation"](https://www.aiboosted.dev/p/getting-better-llm-responses-using-ai-friendly-docs) 16 | 17 | ## Angular 19.2.3 18 | 19 | The Angular documentation is derived from the official source at [Angular GitHub Repository](https://github.com/angular/angular/tree/main/adev/src/content). 20 | 21 | - Complete Angular documentation in a [single file](ai-friendly-docs/angular-19.2.3/angular-full.md), [table of contents](ai-friendly-docs/angular-19.2.3/toc.md) 22 | - [Individual documentation files](ai-friendly-docs/angular-19.2.3/sections) for specific Angular features 23 | 24 | ### Scripts 25 | 26 | The `angular.ts` script is responsible for generating AI-friendly Angular documentation from the official Angular source files. It performs three main operations: 27 | 28 | 1. **Repository Cloning**: Automatically clones the specific version of the Angular repository from GitHub into a local directory. 29 | 30 | 2. **Documentation Generation**: Transforms the original Angular documentation into formats optimized for AI consumption. 31 | 32 | 3. **Vector Embedding Creation**: Creates vector embeddings of the transformed documentation using HuggingFace's embedding models. These embeddings are stored in a local Qdrant vector database (running at localhost:6333), making them readily available for semantic search and retrieval. 33 | 34 | #### Output Files 35 | 36 | The script generates: 37 | 38 | - A comprehensive markdown file containing all Angular documentation 39 | - Individual markdown files for specific Angular features in the `sections` directory 40 | - A table of contents file 41 | - Vector embeddings for search and retrieval 42 | 43 | #### Vector Storage 44 | 45 | The vector embeddings are stored in a Qdrant vector database with the following setup: 46 | 47 | - **Server**: Local Qdrant instance running at `http://localhost:6333` 48 | - **Collection Naming**: Each documentation set gets its own collection (e.g., `angular-19.2.3`) 49 | - **Model**: Uses `Xenova/all-MiniLM-L6-v2` for generating embeddings 50 | - **Chunking**: Documentation is intelligently chunked using a LlamaIndex.TS's Markdown-aware parser that preserves document structure 51 | 52 | #### Directory Structure 53 | 54 | The script maintains the following directory structure: 55 | 56 | ``` 57 | ./ 58 | ├── cloned-repos/ # Repository storage location 59 | │ └── angular-{version}/ # Cloned Angular repository 60 | ├── ai-friendly-docs/ # Generated documentation output 61 | │ └── angular-{version}/ # Version-specific documentation 62 | │ ├── angular-full.md # Complete documentation in one file 63 | │ ├── toc.md # Table of contents 64 | │ └── sections/ # Individual documentation files 65 | ``` 66 | 67 | To use the generated embeddings in your own applications: 68 | 69 | 1. Ensure Qdrant is running locally: `docker run -p 6333:6333 qdrant/qdrant` 70 | 2. Connect to the appropriate collection name corresponding to the documentation version 71 | 3. Perform semantic searches against the indexed content 72 | 73 | ```typescript 74 | // Example code for querying the vector store 75 | import { QdrantVectorStore } from "@llamaindex/qdrant"; 76 | import { VectorStoreIndex } from "llamaindex"; 77 | 78 | // Connect to the existing collection 79 | const vectorStore = new QdrantVectorStore({ 80 | url: "http://localhost:6333", 81 | collectionName: "angular-19.2.3", 82 | }); 83 | 84 | // Create an index from the existing collection 85 | const index = await VectorStoreIndex.fromVectorStore(vectorStore); 86 | const retriever = index.asRetriever({ 87 | similarityTopK: 3, 88 | }); 89 | 90 | // Query the documentation 91 | const nodesWithScore = await retriever.retrieve({ 92 | query: "Are standalone components default in Angular?", 93 | }); 94 | 95 | console.log(nodesWithScore); 96 | ``` 97 | 98 | To run the script: 99 | 100 | ``` 101 | pnpm run generate:angular 102 | ``` 103 | 104 | ## Future Additions 105 | 106 | More open source projects will be added to this collection in the future. 107 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/ssr.md: -------------------------------------------------------------------------------- 1 | # Server-side rendering 2 | 3 | Server-side rendering (SSR) is a process that involves rendering pages on the server, resulting in initial HTML content which contains initial page state. Once the HTML content is delivered to a browser, Angular initializes the application and utilizes the data contained within the HTML. 4 | 5 | ## Why use SSR? 6 | 7 | The main advantages of SSR as compared to client-side rendering (CSR) are: 8 | 9 | - **Improved performance**: SSR can improve the performance of web applications by delivering fully rendered HTML to the client, which the browser can parse and display even before it downloads the application JavaScript. This can be especially beneficial for users on low-bandwidth connections or mobile devices. 10 | - **Improved Core Web Vitals**: SSR results in performance improvements that can be measured using [Core Web Vitals (CWV)](https://web.dev/learn-core-web-vitals/) statistics, such as reduced First Contentful Paint ([FCP](https://developer.chrome.com/en/docs/lighthouse/performance/first-contentful-paint/)) and Largest Contentful Paint ([LCP](https://web.dev/lcp/)), as well as Cumulative Layout Shift ([CLS](https://web.dev/cls/)). 11 | - **Better SEO**: SSR can improve the search engine optimization (SEO) of web applications by making it easier for search engines to crawl and index the content of the application. 12 | 13 | ## Enable server-side rendering 14 | 15 | To create a **new** project with SSR, run: 16 | 17 | ```shell 18 | ng new --ssr 19 | ``` 20 | 21 | To add SSR to an **existing** project, use the Angular CLI `ng add` command. 22 | 23 | ```shell 24 | ng add @angular/ssr 25 | ``` 26 | 27 | Note: Interested in the latest SSR advancements in Angular? Take a look at the developer preview [hybrid rendering APIs](guide/hybrid-rendering). 28 | 29 | These commands create and update application code to enable SSR and adds extra files to the project structure. 30 | 31 | ``` 32 | my-app 33 | |-- server.ts # application server 34 | └── src 35 | |-- app 36 | | └── app.config.server.ts # server application configuration 37 | └── main.server.ts # main server application bootstrapping 38 | ``` 39 | 40 | To verify that the application is server-side rendered, run it locally with `ng serve`. The initial HTML request should contain application content. 41 | 42 | ## Configure server-side rendering 43 | 44 | Note: In Angular v17 and later, `server.ts` is no longer used by `ng serve`. The dev server will use `main.server.ts` directly to perform server side rendering. 45 | 46 | The `server.ts` file configures a Node.js Express server and Angular server-side rendering. `CommonEngine` is used to render an Angular application. 47 | 48 | 49 | 50 | Angular CLI will scaffold an initial server implementation focused on server-side rendering your Angular application. This server can be extended to support other features such as API routes, redirects, static assets, and more. See [Express documentation](https://expressjs.com/) for more details. 51 | 52 | For more information on the APIs, refer to the [`CommonEngine` API reference](api/ssr/node/CommonEngineRenderOptions). 53 | 54 | ## Hydration 55 | 56 | Hydration is the process that restores the server side rendered application on the client. This includes things like reusing the server rendered DOM structures, persisting the application state, transferring application data that was retrieved already by the server, and other processes. Hydration is enabled by default when you use SSR. You can find more info in [the hydration guide](guide/hydration). 57 | 58 | ## Caching data when using HttpClient 59 | 60 | [`HttpClient`](api/common/http/HttpClient) cached outgoing network requests when running on the server. This information is serialized and transferred to the browser as part of the initial HTML sent from the server. In the browser, `HttpClient` checks whether it has data in the cache and if so, reuses it instead of making a new HTTP request during initial application rendering. `HttpClient` stops using the cache once an application becomes [stable](api/core/ApplicationRef#isStable) while running in a browser. 61 | 62 | By default, `HttpClient` caches all `HEAD` and `GET` requests which don't contain `Authorization` or `Proxy-Authorization` headers. You can override those settings by using [`withHttpTransferCacheOptions`](api/platform-browser/withHttpTransferCacheOptions) when providing hydration. 63 | 64 | ```typescript 65 | bootstrapApplication(AppComponent, { 66 | providers: [ 67 | provideClientHydration(withHttpTransferCacheOptions({ 68 | includePostRequests: true 69 | })) 70 | ] 71 | }); 72 | ``` 73 | 74 | ## Authoring server-compatible components 75 | 76 | Some common browser APIs and capabilities might not be available on the server. Applications cannot make use of browser-specific global objects like `window`, `document`, `navigator`, or `location` as well as certain properties of `HTMLElement`. 77 | 78 | In general, code which relies on browser-specific symbols should only be executed in the browser, not on the server. This can be enforced through the [`afterRender`](api/core/afterRender) and [`afterNextRender`](api/core/afterNextRender) lifecycle hooks. These are only executed on the browser and skipped on the server. 79 | 80 | ```angular-ts 81 | import { Component, ViewChild, afterNextRender } from '@angular/core'; 82 | 83 | @Component({ 84 | selector: 'my-cmp', 85 | template: `{{ ... }}`, 86 | }) 87 | export class MyComponent { 88 | @ViewChild('content') contentRef: ElementRef; 89 | 90 | constructor() { 91 | afterNextRender(() => { 92 | // Safe to check `scrollHeight` because this will only run in the browser, not the server. 93 | console.log('content height: ' + this.contentRef.nativeElement.scrollHeight); 94 | }); 95 | } 96 | } 97 | ``` 98 | 99 | ## Using Angular Service Worker 100 | 101 | If you are using Angular on the server in combination with the Angular service worker, the behavior deviates from the normal server-side rendering behavior. The initial server request will be rendered on the server as expected. However, after that initial request, subsequent requests are handled by the service worker and always client-side rendered. 102 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/cli.md: -------------------------------------------------------------------------------- 1 | # Cli 2 | 3 | (From index.md) 4 | 5 | ## CLI Overview and Command Reference 6 | 7 | The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications directly from a command shell. 8 | 9 | ### Installing Angular CLI 10 | 11 | Major versions of Angular CLI follow the supported major version of Angular, but minor versions can be released separately. 12 | 13 | Install the CLI using the `npm` package manager: 14 | 15 | 16 | 17 | npm install -g @angular/cli 18 | 19 | 20 | 21 | For details about changes between versions, and information about updating from previous releases, see the Releases tab on GitHub: https://github.com/angular/angular-cli/releases 22 | 23 | ### Basic workflow 24 | 25 | Invoke the tool on the command line through the `ng` executable. 26 | Online help is available on the command line. 27 | Enter the following to list commands or options for a given command \(such as [new](cli/new)\) with a short description. 28 | 29 | 30 | 31 | ng --help 32 | ng new --help 33 | 34 | 35 | 36 | To create, build, and serve a new, basic Angular project on a development server, go to the parent directory of your new workspace use the following commands: 37 | 38 | 39 | 40 | ng new my-first-project 41 | cd my-first-project 42 | ng serve 43 | 44 | 45 | 46 | In your browser, open http://localhost:4200/ to see the new application run. 47 | When you use the [ng serve](cli/serve) command to build an application and serve it locally, the server automatically rebuilds the application and reloads the page when you change any of the source files. 48 | 49 |
50 | 51 | When you run `ng new my-first-project` a new folder, named `my-first-project`, will be created in the current working directory. 52 | Since you want to be able to create files inside that folder, make sure you have sufficient rights in the current working directory before running the command. 53 | 54 | If the current working directory is not the right place for your project, you can change to a more appropriate directory by running `cd `. 55 | 56 |
57 | 58 | ### Workspaces and project files 59 | 60 | The [ng new](cli/new) command creates an *Angular workspace* folder and generates a new application skeleton. 61 | A workspace can contain multiple applications and libraries. 62 | The initial application created by the [ng new](cli/new) command is at the top level of the workspace. 63 | When you generate an additional application or library in a workspace, it goes into a `projects/` subfolder. 64 | 65 | A newly generated application contains the source files for a root module, with a root component and template. 66 | Each application has a `src` folder that contains the logic, data, and assets. 67 | 68 | You can edit the generated files directly, or add to and modify them using CLI commands. 69 | Use the [ng generate](cli/generate) command to add new files for additional components and services, and code for new pipes, directives, and so on. 70 | Commands such as [add](cli/add) and [generate](cli/generate), which create or operate on applications and libraries, must be executed from within a workspace or project folder. 71 | 72 | * See more about the [Workspace file structure](guide/file-structure). 73 | 74 | #### Workspace and project configuration 75 | 76 | A single workspace configuration file, `angular.json`, is created at the top level of the workspace. 77 | This is where you can set per-project defaults for CLI command options, and specify configurations to use when the CLI builds a project for different targets. 78 | 79 | The [ng config](cli/config) command lets you set and retrieve configuration values from the command line, or you can edit the `angular.json` file directly. 80 | 81 |
82 | 83 | **NOTE**:
84 | Option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands must be dash-case. 85 | 86 |
87 | 88 | * See more about [Workspace Configuration](guide/workspace-config). 89 | 90 | ### CLI command-language syntax 91 | 92 | Command syntax is shown as follows: 93 | 94 | `ng` ** ** [*optional-arg*] `[options]` 95 | 96 | * Most commands, and some options, have aliases. 97 | Aliases are shown in the syntax statement for each command. 98 | 99 | * Option names are prefixed with a double dash \(`--`\) characters. 100 | Option aliases are prefixed with a single dash \(`-`\) character. 101 | Arguments are not prefixed. 102 | For example: 103 | 104 | 105 | 106 | ng build my-app -c production 107 | 108 | 109 | 110 | * Typically, the name of a generated artifact can be given as an argument to the command or specified with the `--name` option. 111 | 112 | * Arguments and option names must be given in [dash-case](guide/glossary#case-types). 113 | For example: `--my-option-name` 114 | 115 | #### Boolean options 116 | 117 | Boolean options have two forms: `--this-option` sets the flag to `true`, `--no-this-option` sets it to `false`. 118 | If neither option is supplied, the flag remains in its default state, as listed in the reference documentation. 119 | 120 | #### Array options 121 | 122 | Array options can be provided in two forms: `--option value1 value2` or `--option value1 --option value2`. 123 | 124 | #### Relative paths 125 | 126 | Options that specify files can be given as absolute paths, or as paths relative to the current working directory, which is generally either the workspace or project root. 127 | 128 | #### Schematics 129 | 130 | The [ng generate](cli/generate) and [ng add](cli/add) commands take, as an argument, the artifact or library to be generated or added to the current project. 131 | In addition to any general options, each artifact or library defines its own options in a *schematic*. 132 | Schematic options are supplied to the command in the same format as immediate command options. 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | @reviewed 2022-02-28 141 | 142 | --- -------------------------------------------------------------------------------- /src/features/angular-docs/generateAiFriendlyAngularDocs.ts: -------------------------------------------------------------------------------- 1 | import { copySingleFile } from "@shared/file-utils/copySingleFile"; 2 | import { consolidateDirectory } from "@shared/markdown/consolidateDirectory"; 3 | import { generateSingleDocFile } from "@shared/markdown/generateSingleDocFile"; 4 | import { generateTableOfContents } from "@shared/markdown/generateTableOfContents"; 5 | import * as fs from "fs"; 6 | import * as path from "path"; 7 | 8 | /** 9 | * Parameters for generating AI-friendly Angular documentation 10 | */ 11 | export type GenerateAiFriendlyAngularDocsParams = { 12 | /** 13 | * Root directory containing source documentation files 14 | */ 15 | sourceDir: string; 16 | 17 | /** 18 | * Destination directory for consolidated documentation 19 | */ 20 | targetDir: string; 21 | }; 22 | 23 | /** 24 | * Orchestrates the AI-friendly documentation generation process for Angular. 25 | * 26 | * @param params - Generation parameters including source and target directories 27 | * @returns Promise that resolves when documentation generation is complete 28 | * 29 | * @description Manages the end-to-end AI-friendly documentation generation workflow, 30 | * including directory preparation, file processing, and generation of 31 | * comprehensive and table of contents documents. 32 | */ 33 | export async function generateAiFriendlyAngularDocs( 34 | params: GenerateAiFriendlyAngularDocsParams 35 | ): Promise { 36 | const { sourceDir, targetDir } = params; 37 | console.log("Starting AI-friendly Angular documentation generation...\n"); 38 | 39 | const sectionsDir = path.join(targetDir, "sections"); 40 | const singleFilePath = path.join(targetDir, "angular-full.md"); 41 | const tocFilePath = path.join(targetDir, "toc.md"); 42 | 43 | // Clean and recreate target directory 44 | if (fs.existsSync(targetDir)) { 45 | console.log(`Removing existing target directory: ${targetDir}`); 46 | fs.rmSync(targetDir, { recursive: true }); 47 | } 48 | 49 | // Create the target directory 50 | fs.mkdirSync(targetDir, { recursive: true }); 51 | console.log(`Created target directory: ${targetDir}`); 52 | 53 | // Create sections directory 54 | fs.mkdirSync(sectionsDir, { recursive: true }); 55 | console.log(`Created sections directory: ${sectionsDir}`); 56 | 57 | // Track all generated files for later combining into a single file 58 | const sectionFiles: string[] = []; 59 | 60 | // 1. CLI Documentation 61 | consolidateDirectory({ 62 | sourceDir: path.join(sourceDir, "cli"), 63 | targetFile: "cli.md", 64 | sectionsDir, 65 | }); 66 | sectionFiles.push("cli.md"); 67 | 68 | // 2. RxJS Interop Documentation 69 | consolidateDirectory({ 70 | sourceDir: path.join(sourceDir, "ecosystem", "rxjs-interop"), 71 | targetFile: "rxjs-interop.md", 72 | sectionsDir, 73 | }); 74 | sectionFiles.push("rxjs-interop.md"); 75 | 76 | // 3. Service Workers Documentation 77 | consolidateDirectory({ 78 | sourceDir: path.join(sourceDir, "ecosystem", "service-workers"), 79 | targetFile: "service-workers.md", 80 | sectionsDir, 81 | }); 82 | sectionFiles.push("service-workers.md"); 83 | 84 | // 4. Custom Build Pipeline Documentation 85 | copySingleFile({ 86 | sourcePath: path.join(sourceDir, "ecosystem", "custom-build-pipeline.md"), 87 | targetFile: "custom-build-pipeline.md", 88 | sectionsDir, 89 | }); 90 | sectionFiles.push("custom-build-pipeline.md"); 91 | 92 | // 5. Web Workers Documentation 93 | copySingleFile({ 94 | sourcePath: path.join(sourceDir, "ecosystem", "web-workers.md"), 95 | targetFile: "web-workers.md", 96 | sectionsDir, 97 | }); 98 | sectionFiles.push("web-workers.md"); 99 | 100 | // 6. Introduction Documentation 101 | consolidateDirectory({ 102 | sourceDir: path.join(sourceDir, "introduction"), 103 | targetFile: "introduction.md", 104 | sectionsDir, 105 | }); 106 | sectionFiles.push("introduction.md"); 107 | 108 | // 7. Reference Documentation 109 | consolidateDirectory({ 110 | sourceDir: path.join(sourceDir, "reference"), 111 | targetFile: "reference.md", 112 | sectionsDir, 113 | }); 114 | sectionFiles.push("reference.md"); 115 | 116 | // 8. CLI Tools Documentation 117 | consolidateDirectory({ 118 | sourceDir: path.join(sourceDir, "tools", "cli"), 119 | targetFile: "cli-tools.md", 120 | sectionsDir, 121 | }); 122 | sectionFiles.push("cli-tools.md"); 123 | 124 | // 9. Libraries Documentation 125 | consolidateDirectory({ 126 | sourceDir: path.join(sourceDir, "tools", "libraries"), 127 | targetFile: "libraries.md", 128 | sectionsDir, 129 | }); 130 | sectionFiles.push("libraries.md"); 131 | 132 | // 10. DevTools Documentation 133 | copySingleFile({ 134 | sourcePath: path.join(sourceDir, "tools", "devtools.md"), 135 | targetFile: "devtools.md", 136 | sectionsDir, 137 | }); 138 | sectionFiles.push("devtools.md"); 139 | 140 | // 11. Language Service Documentation 141 | copySingleFile({ 142 | sourcePath: path.join(sourceDir, "tools", "language-service.md"), 143 | targetFile: "language-service.md", 144 | sectionsDir, 145 | }); 146 | sectionFiles.push("language-service.md"); 147 | 148 | // 12. Best Practices Documentation 149 | consolidateDirectory({ 150 | sourceDir: path.join(sourceDir, "best-practices"), 151 | targetFile: "best-practices.md", 152 | sectionsDir, 153 | }); 154 | sectionFiles.push("best-practices.md"); 155 | 156 | // 13. Guide Documentation 157 | const guideDir = path.join(sourceDir, "guide"); 158 | 159 | if (fs.existsSync(guideDir)) { 160 | const guideItems = fs.readdirSync(guideDir, { withFileTypes: true }); 161 | 162 | for (const item of guideItems) { 163 | const fullPath = path.join(guideDir, item.name); 164 | 165 | if (item.isDirectory()) { 166 | // For directories, create a combined file 167 | const targetFileName = `guide-${item.name}.md`; 168 | consolidateDirectory({ 169 | sourceDir: fullPath, 170 | targetFile: targetFileName, 171 | sectionsDir, 172 | }); 173 | sectionFiles.push(targetFileName); 174 | } else if (item.name.endsWith(".md")) { 175 | // For markdown files, create individual files with the same name 176 | copySingleFile({ 177 | sourcePath: fullPath, 178 | targetFile: item.name, 179 | sectionsDir, 180 | }); 181 | sectionFiles.push(item.name); 182 | } 183 | } 184 | } else { 185 | console.error(`Guide directory not found: ${guideDir}`); 186 | } 187 | 188 | console.log("\nAI-friendly Angular documentation generation complete!"); 189 | 190 | // After processing all sections, generate a single combined file 191 | generateSingleDocFile({ 192 | sectionFiles, 193 | sectionsDir, 194 | outputPath: singleFilePath, 195 | }); 196 | 197 | // Generate a table of contents file 198 | generateTableOfContents({ 199 | sectionFiles, 200 | sectionsDir, 201 | mainDocPath: singleFilePath, 202 | outputPath: tocFilePath, 203 | }); 204 | } 205 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/rxjs-interop.md: -------------------------------------------------------------------------------- 1 | # Rxjs Interop 2 | 3 | (From output-interop.md) 4 | 5 | ## RxJS interop with component and directive outputs 6 | 7 | Tip: This guide assumes you're familiar with [component and directive outputs](guide/components/outputs). 8 | 9 | The `@angular/rxjs-interop` package offers two APIs related to component and directive outputs. 10 | 11 | ### Creating an output based on an RxJs Observable 12 | 13 | The `outputFromObservable` lets you create a component or directive output that emits based on an RxJS observable: 14 | 15 | 16 | import {Directive} from '@angular/core'; 17 | import {outputFromObservable} from '@angular/core/rxjs-interop'; 18 | 19 | @Directive({/*...*/}) 20 | class Draggable { 21 | pointerMoves$: Observable = listenToPointerMoves(); 22 | 23 | // Whenever `pointerMoves$` emits, the `pointerMove` event fires. 24 | pointerMove = outputFromObservable(this.pointerMoves$); 25 | } 26 | 27 | 28 | The `outputFromObservable` function has special meaning to the Angular compiler. **You may only call `outputFromObservable` in component and directive property initializers.** 29 | 30 | When you `subscribe` to the output, Angular automatically forwards the subscription to the underlying observable. Angular stops forwarding values when the component or directive is destroyed. 31 | 32 | HELPFUL: Consider using `output()` directly if you can emit values imperatively. 33 | 34 | ### Creating an RxJS Observable from a component or directive output 35 | 36 | The `outputToObservable` function lets you create an RxJS observable from a component output. 37 | 38 | 39 | import {outputToObservable} from '@angular/core/rxjs-interop'; 40 | 41 | @Component(/*...*/) 42 | class CustomSlider { 43 | valueChange = output(); 44 | } 45 | 46 | // Instance reference to `CustomSlider`. 47 | const slider: CustomSlider = createSlider(); 48 | 49 | outputToObservable(slider.valueChange) // Observable 50 | .pipe(...) 51 | .subscribe(...); 52 | 53 | 54 | HELPFUL: Consider using the `subscribe` method on `OutputRef` directly if it meets your needs. 55 | 56 | --- 57 | 58 | 59 | (From signals-interop.md) 60 | 61 | ## RxJS interop with Angular signals 62 | 63 | IMPORTANT: The RxJS Interop package is available for [developer preview](reference/releases#developer-preview). It's ready for you to try, but it might change before it is stable. 64 | 65 | The `@angular/rxjs-interop` package offers APIs that help you integrate RxJS and Angular signals. 66 | 67 | ### Create a signal from an RxJs Observable with `toSignal` 68 | 69 | Use the `toSignal` function to create a signal which tracks the value of an Observable. It behaves similarly to the `async` pipe in templates, but is more flexible and can be used anywhere in an application. 70 | 71 | ```ts 72 | import { Component } from '@angular/core'; 73 | import { AsyncPipe } from '@angular/common'; 74 | import { interval } from 'rxjs'; 75 | import { toSignal } from '@angular/core/rxjs-interop'; 76 | 77 | @Component({ 78 | template: `{{ counter() }}`, 79 | }) 80 | export class Ticker { 81 | counterObservable = interval(1000); 82 | 83 | // Get a `Signal` representing the `counterObservable`'s value. 84 | counter = toSignal(this.counterObservable, {initialValue: 0}); 85 | } 86 | ``` 87 | 88 | Like the `async` pipe, `toSignal` subscribes to the Observable immediately, which may trigger side effects. The subscription created by `toSignal` automatically unsubscribes from the given Observable when the component or service which calls `toSignal` is destroyed. 89 | 90 | IMPORTANT: `toSignal` creates a subscription. You should avoid calling it repeatedly for the same Observable, and instead reuse the signal it returns. 91 | 92 | #### Injection context 93 | 94 | `toSignal` by default needs to run in an [injection context](guide/di/dependency-injection-context), such as during construction of a component or service. If an injection context is not available, you can manually specify the `Injector` to use instead. 95 | 96 | #### Initial values 97 | 98 | Observables may not produce a value synchronously on subscription, but signals always require a current value. There are several ways to deal with this "initial" value of `toSignal` signals. 99 | 100 | ##### The `initialValue` option 101 | 102 | As in the example above, you can specify an `initialValue` option with the value the signal should return before the Observable emits for the first time. 103 | 104 | ##### `undefined` initial values 105 | 106 | If you don't provide an `initialValue`, the resulting signal will return `undefined` until the Observable emits. This is similar to the `async` pipe's behavior of returning `null`. 107 | 108 | ##### The `requireSync` option 109 | 110 | Some Observables are guaranteed to emit synchronously, such as `BehaviorSubject`. In those cases, you can specify the `requireSync: true` option. 111 | 112 | When `requiredSync` is `true`, `toSignal` enforces that the Observable emits synchronously on subscription. This guarantees that the signal always has a value, and no `undefined` type or initial value is required. 113 | 114 | #### `manualCleanup` 115 | 116 | By default, `toSignal` automatically unsubscribes from the Observable when the component or service that creates it is destroyed. 117 | 118 | To override this behavior, you can pass the `manualCleanup` option. You can use this setting for Observables that complete themselves naturally. 119 | 120 | #### Error and Completion 121 | 122 | If an Observable used in `toSignal` produces an error, that error is thrown when the signal is read. 123 | 124 | If an Observable used in `toSignal` completes, the signal continues to return the most recently emitted value before completion. 125 | 126 | ### Create an RxJS Observable from a signal with `toObservable` 127 | 128 | Use the `toObservable` utility to create an `Observable` which tracks the value of a signal. The signal's value is monitored with an `effect` which emits the value to the Observable when it changes. 129 | 130 | ```ts 131 | import { Component, signal } from '@angular/core'; 132 | import { toObservable } from '@angular/core/rxjs-interop'; 133 | 134 | @Component(...) 135 | export class SearchResults { 136 | query: Signal = inject(QueryService).query; 137 | query$ = toObservable(this.query); 138 | 139 | results$ = this.query$.pipe( 140 | switchMap(query => this.http.get('/search?q=' + query )) 141 | ); 142 | } 143 | ``` 144 | 145 | As the `query` signal changes, the `query$` Observable emits the latest query and triggers a new HTTP request. 146 | 147 | #### Injection context 148 | 149 | `toObservable` by default needs to run in an [injection context](guide/di/dependency-injection-context), such as during construction of a component or service. If an injection context is not available, you can manually specify the `Injector` to use instead. 150 | 151 | #### Timing of `toObservable` 152 | 153 | `toObservable` uses an effect to track the value of the signal in a `ReplaySubject`. On subscription, the first value (if available) may be emitted synchronously, and all subsequent values will be asynchronous. 154 | 155 | Unlike Observables, signals never provide a synchronous notification of changes. Even if you update a signal's value multiple times, `toObservable` will only emit the value after the signal stabilizes. 156 | 157 | ```ts 158 | const obs$ = toObservable(mySignal); 159 | obs$.subscribe(value => console.log(value)); 160 | 161 | mySignal.set(1); 162 | mySignal.set(2); 163 | mySignal.set(3); 164 | ``` 165 | 166 | Here, only the last value (3) will be logged. 167 | 168 | --- -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/guide-ngmodules.md: -------------------------------------------------------------------------------- 1 | # Guide Ngmodules 2 | 3 | (From overview.md) 4 | 5 | ## NgModules 6 | 7 | IMPORTANT: The Angular team recommends using [standalone components](guide/components/anatomy-of-components#-imports-in-the-component-decorator) instead of `NgModule` for all new code. Use this guide to understand existing code built with `@NgModule`. 8 | 9 | An NgModule is a class marked by the `@NgModule` decorator. This decorator accepts *metadata* that tells Angular how to compile component templates and configure dependency injection. 10 | 11 | ```typescript 12 | import {NgModule} from '@angular/core'; 13 | 14 | @NgModule({ 15 | // Metadata goes here 16 | }) 17 | export class CustomMenuModule { } 18 | ``` 19 | 20 | An NgModule has two main responsibilities: 21 | * Declaring components, directives, and pipes that belong to the NgModule 22 | * Add providers to the injector for components, directives, and pipes that import the NgModule 23 | 24 | ### Declarations 25 | 26 | The `declarations` property of the `@NgModule` metadata declares the components, directives, and pipes that belong to the NgModule. 27 | 28 | ```typescript 29 | @NgModule({ 30 | /* ... */ 31 | // CustomMenu and CustomMenuItem are components. 32 | declarations: [CustomMenu, CustomMenuItem], 33 | }) 34 | export class CustomMenuModule { } 35 | ``` 36 | 37 | In the example above, the components `CustomMenu` and `CustomMenuItem` belong to `CustomMenuModule`. 38 | 39 | The `declarations` property additionally accepts _arrays_ of components, directives, and pipes. These arrays, in turn, may also contain other arrays. 40 | 41 | ```typescript 42 | const MENU_COMPONENTS = [CustomMenu, CustomMenuItem]; 43 | const WIDGETS = [MENU_COMPONENTS, CustomSlider]; 44 | 45 | @NgModule({ 46 | /* ... */ 47 | // This NgModule declares all of CustomMenu, CustomMenuItem, 48 | // CustomSlider, and CustomCheckbox. 49 | declarations: [WIDGETS, CustomCheckbox], 50 | }) 51 | export class CustomMenuModule { } 52 | ``` 53 | 54 | If Angular discovers any components, directives, or pipes declared in more than one NgModule, it reports an error. 55 | 56 | Any components, directives, or pipes must be explicitly marked as `standalone: false` in order to be declared in an NgModule. 57 | 58 | ```typescript 59 | @Component({ 60 | // Mark this component as `standalone: false` so that it can be declared in an NgModule. 61 | standalone: false, 62 | /* ... */ 63 | }) 64 | export class CustomMenu { /* ... */ } 65 | ``` 66 | 67 | #### imports 68 | 69 | Components declared in an NgModule may depend on other components, directives, and pipes. Add these dependencies to the `imports` property of the `@NgModule` metadata. 70 | 71 | ```typescript 72 | @NgModule({ 73 | /* ... */ 74 | // CustomMenu and CustomMenuItem depend on the PopupTrigger and SelectorIndicator components. 75 | imports: [PopupTrigger, SelectionIndicator], 76 | declarations: [CustomMenu, CustomMenuItem], 77 | }) 78 | export class CustomMenuModule { } 79 | ``` 80 | 81 | The `imports` array accepts other NgModules, as well as standalone components, directives, and pipes. 82 | 83 | #### exports 84 | 85 | An NgModule can _export_ its declared components, directives, and pipes such that they're available to other components and NgModules. 86 | 87 | ```typescript 88 | @NgModule({ 89 | imports: [PopupTrigger, SelectionIndicator], 90 | declarations: [CustomMenu, CustomMenuItem], 91 | 92 | // Make CustomMenu and CustomMenuItem available to 93 | // components and NgModules that import CustomMenuModule. 94 | exports: [CustomMenu, CustomMenuItem], 95 | }) 96 | export class CustomMenuModule { } 97 | ``` 98 | 99 | The `exports` property is not limited to declarations, however. An NgModule can also export any other components, directives, pipes, and NgModules that it imports. 100 | 101 | ```typescript 102 | @NgModule({ 103 | imports: [PopupTrigger, SelectionIndicator], 104 | declarations: [CustomMenu, CustomMenuItem], 105 | 106 | // Also make PopupTrigger available to any component or NgModule that imports CustomMenuModule. 107 | exports: [CustomMenu, CustomMenuItem, PopupTrigger], 108 | }) 109 | export class CustomMenuModule { } 110 | ``` 111 | 112 | ### `NgModule` providers 113 | 114 | Tip: See the [Dependency Injection guide](guide/di) for information on dependency injection and providers. 115 | 116 | An `NgModule` can specify `providers` for injected dependencies. These providers are available to: 117 | * Any standalone component, directive, or pipe that imports the NgModule, and 118 | * The `declarations` and `providers` of any _other_ NgModule that imports the NgModule. 119 | 120 | ```typescript 121 | @NgModule({ 122 | imports: [PopupTrigger, SelectionIndicator], 123 | declarations: [CustomMenu, CustomMenuItem], 124 | 125 | // Provide the OverlayManager service 126 | providers: [OverlayManager], 127 | /* ... */ 128 | }) 129 | export class CustomMenuModule { } 130 | 131 | @NgModule({ 132 | imports: [CustomMenuModule], 133 | declarations: [UserProfile], 134 | providers: [UserDataClient], 135 | }) 136 | export class UserProfileModule { } 137 | ``` 138 | 139 | In the example above: 140 | * The `CustomMenuModule` provides `OverlayManager`. 141 | * The `CustomMenu` and `CustomMenuItem` components can inject `OverlayManager` because they're declared in `CustomMenuModule`. 142 | * `UserProfile` can inject `OverlayManager` because its NgModule imports `CustomMenuModule`. 143 | * `UserDataClient` can inject `OverlayManager` because its NgModule imports `CustomMenuModule`. 144 | 145 | #### The `forRoot` and `forChild` pattern 146 | 147 | Some NgModules define a static `forRoot` method that accepts some configuration and returns an array of providers. The name "`forRoot`" is a convention that indicates that these providers are intended to be added exclusively to the _root_ of your application during bootstrap. 148 | 149 | Any providers included in this way are eagerly loaded, increasing the JavaScript bundle size of your initial page load. 150 | 151 | ```typescript 152 | boorstrapApplication(MyApplicationRoot, { 153 | providers: [ 154 | CustomMenuModule.forRoot(/* some config */), 155 | ], 156 | }); 157 | ``` 158 | 159 | Similarly, some NgModules may define a static `forChild` that indicates the providers are intended to be added to components within your application hierarchy. 160 | 161 | ```typescript 162 | @Component({ 163 | /* ... */ 164 | providers: [ 165 | CustomMenuModule.forChild(/* some config */), 166 | ], 167 | }) 168 | export class UserProfile { /* ... */ } 169 | ``` 170 | 171 | ### Bootstrapping an application 172 | 173 | IMPORTANT: The Angular team recommends using [bootstrapApplication](api/platform-browser/bootstrapApplication) instead of `bootstrapModule` for all new code. Use this guide to understand existing applications bootstrapped with `@NgModule`. 174 | 175 | The `@NgModule` decorator accepts an optional `bootstrap` array that may contain one or more components. 176 | 177 | You can use the [`bootstrapModule`](https://angular.dev/api/core/PlatformRef#bootstrapModule) method from either [`platformBrowser`](api/platform-browser/platformBrowser) or [`platformServer`](api/platform-server/platformServer) to start an Angular application. When run, this function locates any elements on the page with a CSS selector that matches the listed componet(s) and renders those components on the page. 178 | 179 | ```typescript 180 | import {platformBrowser} from '@angular/platform-browser'; 181 | 182 | @NgModule({ 183 | bootstrap: [MyApplication], 184 | }) 185 | export class MyApplicationModule { } 186 | 187 | platformBrowser().bootstrapModule(MyApplicationModule); 188 | ``` 189 | 190 | Components listed in `bootstrap` are automatically included in the NgModule's declarations. 191 | 192 | When you bootstrap an application from an NgModule, the collected `providers` of this module and all of the `providers` of its `imports` are eagerly loaded and available to inject for the entire application. 193 | 194 | --- -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/language-service.md: -------------------------------------------------------------------------------- 1 | # Angular Language Service 2 | 3 | The Angular Language Service provides code editors with a way to get completions, errors, hints, and navigation inside Angular templates. 4 | It works with external templates in separate HTML files, and also with in-line templates. 5 | 6 | ## Configuring compiler options for the Angular Language Service 7 | 8 | To enable the latest Language Service features, set the `strictTemplates` option in `tsconfig.json` by setting `strictTemplates` to `true`, as shown in the following example: 9 | 10 | 11 | 12 | "angularCompilerOptions": { 13 | "strictTemplates": true 14 | } 15 | 16 | 17 | 18 | For more information, see the [Angular compiler options](reference/configs/angular-compiler-options) guide. 19 | 20 | ## Features 21 | 22 | Your editor autodetects that you are opening an Angular file. 23 | It then uses the Angular Language Service to read your `tsconfig.json` file, find all the templates you have in your application, and then provide language services for any templates that you open. 24 | 25 | Language services include: 26 | 27 | * Completions lists 28 | * AOT Diagnostic messages 29 | * Quick info 30 | * Go to definition 31 | 32 | ### Autocompletion 33 | 34 | Autocompletion can speed up your development time by providing you with contextual possibilities and hints as you type. 35 | This example shows autocomplete in an interpolation. 36 | As you type it out, you can press tab to complete. 37 | 38 | autocompletion 39 | 40 | There are also completions within elements. 41 | Any elements you have as a component selector will show up in the completion list. 42 | 43 | ### Error checking 44 | 45 | The Angular Language Service can forewarn you of mistakes in your code. 46 | In this example, Angular doesn't know what `orders` is or where it comes from. 47 | 48 | error checking 49 | 50 | ### Quick info and navigation 51 | 52 | The quick-info feature lets you hover to see where components, directives, and modules come from. 53 | You can then click "Go to definition" or press F12 to go directly to the definition. 54 | 55 | navigation 56 | 57 | ## Angular Language Service in your editor 58 | 59 | Angular Language Service is currently available as an extension for [Visual Studio Code](https://code.visualstudio.com), [WebStorm](https://www.jetbrains.com/webstorm), [Sublime Text](https://www.sublimetext.com) and [Eclipse IDE](https://www.eclipse.org/eclipseide). 60 | 61 | ### Visual Studio Code 62 | 63 | In [Visual Studio Code](https://code.visualstudio.com), install the extension from the [Extensions: Marketplace](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template). 64 | Open the marketplace from the editor using the Extensions icon on the left menu pane, or use VS Quick Open \(⌘+P on Mac, CTRL+P on Windows\) and type "? ext". 65 | In the marketplace, search for Angular Language Service extension, and click the **Install** button. 66 | 67 | The Visual Studio Code integration with the Angular language service is maintained and distributed by the Angular team. 68 | 69 | ### Visual Studio 70 | 71 | In [Visual Studio](https://visualstudio.microsoft.com), install the extension from the [Extensions: Marketplace](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.AngularLanguageService). 72 | Open the marketplace from the editor selecting Extensions on the top menu pane, and then selecting Manage Extensions. 73 | In the marketplace, search for Angular Language Service extension, and click the **Install** button. 74 | 75 | The Visual Studio integration with the Angular language service is maintained and distributed by Microsoft with help from the Angular team. 76 | Check out the project [here](https://github.com/microsoft/vs-ng-language-service). 77 | 78 | ### WebStorm 79 | 80 | In [WebStorm](https://www.jetbrains.com/webstorm), enable the plugin [Angular and AngularJS](https://plugins.jetbrains.com/plugin/6971-angular-and-angularjs). 81 | 82 | Since WebStorm 2019.1, the `@angular/language-service` is not required anymore and should be removed from your `package.json`. 83 | 84 | ### Sublime Text 85 | 86 | In [Sublime Text](https://www.sublimetext.com), the Language Service supports only in-line templates when installed as a plug-in. 87 | You need a custom Sublime plug-in \(or modifications to the current plug-in\) for completions in HTML files. 88 | 89 | To use the Language Service for in-line templates, you must first add an extension to allow TypeScript, then install the Angular Language Service plug-in. 90 | Starting with TypeScript 2.3, TypeScript has a plug-in model that the language service can use. 91 | 92 | 1. Install the latest version of TypeScript in a local `node_modules` directory: 93 | 94 | 95 | 96 | npm install --save-dev typescript 97 | 98 | 99 | 100 | 1. Install the Angular Language Service package in the same location: 101 | 102 | 103 | 104 | npm install --save-dev @angular/language-service 105 | 106 | 107 | 108 | 1. Once the package is installed, add the following to the `"compilerOptions"` section of your project's `tsconfig.json`. 109 | 110 | 111 | 112 | "plugins": [ 113 | {"name": "@angular/language-service"} 114 | ] 115 | 116 | 117 | 118 | 1. In your editor's user preferences \(`Cmd+,` or `Ctrl+,`\), add the following: 119 | 120 | 121 | 122 | "typescript-tsdk": "/node_modules/typescript/lib" 123 | 124 | 125 | 126 | This lets the Angular Language Service provide diagnostics and completions in `.ts` files. 127 | 128 | ### Eclipse IDE 129 | 130 | Either directly install the "Eclipse IDE for Web and JavaScript developers" package which comes with the Angular Language Server included, or from other Eclipse IDE packages, use Help > Eclipse Marketplace to find and install [Eclipse Wild Web Developer](https://marketplace.eclipse.org/content/wild-web-developer-html-css-javascript-typescript-nodejs-angular-json-yaml-kubernetes-xml). 131 | 132 | ### Neovim 133 | 134 | Angular language service can be used with Neovim by using the [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) plugin. 135 | 136 | 1. [Install nvim-lspconfig](https://github.com/neovim/nvim-lspconfig?tab=readme-ov-file#install) 137 | 138 | 2. [Configure angularls for nvim-lspconfig](https://github.com/neovim/nvim-lspconfig/blob/master/doc/configs.md#angularls) 139 | 140 | ## How the Language Service works 141 | 142 | When you use an editor with a language service, the editor starts a separate language-service process and communicates with it through an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call), using the [Language Server Protocol](https://microsoft.github.io/language-server-protocol). 143 | When you type into the editor, the editor sends information to the language-service process to track the state of your project. 144 | 145 | When you trigger a completion list within a template, the editor first parses the template into an HTML [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree). 146 | The Angular compiler interprets that tree to determine the context: which module the template is part of, the current scope, the component selector, and where your cursor is in the template AST. 147 | It can then determine the symbols that could potentially be at that position. 148 | 149 | It's a little more involved if you are in an interpolation. 150 | If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer. 151 | The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`". 152 | That's when the template parser produces an expression AST, which resides within the template AST. 153 | The Angular Language Services then looks at `data.---` within its context, asks the TypeScript Language Service what the members of `data` are, and returns the list of possibilities. 154 | 155 | ## More information 156 | 157 | * For more in-depth information on the implementation, see the [Angular Language Service source](https://github.com/angular/angular/blob/main/packages/language-service/src) 158 | * For more on the design considerations and intentions, see [design documentation here](https://github.com/angular/vscode-ng-language-service/wiki/Design) -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/zoneless.md: -------------------------------------------------------------------------------- 1 | # Angular without ZoneJS (Zoneless) 2 | 3 | ## Why use Zoneless? 4 | 5 | The main advantages to removing ZoneJS as a dependency are: 6 | 7 | - **Improved performance**: ZoneJS uses DOM events and async tasks as indicators of when application state _might_ have updated and subsequently triggers application synchronization to run change detection on the application's views. ZoneJS does not have any insight into whether application state actually changed and so this synchronization is triggered more frequently than necessary. 8 | - **Improved Core Web Vitals**: ZoneJS brings a fair amount of overhead, both in payload size and in startup time cost. 9 | - **Improved debugging experience**: ZoneJS makes debugging code more difficult. Stack traces are harder to understand with ZoneJS. It's also difficult to understand when code breaks as a result of being outside the Angular Zone. 10 | - **Better ecosystem compatibility**: ZoneJS works by patching browser APIs but does not automatically have patches for every new browser API. Some APIs cannot be patched effectively, such as `async`/`await`, and have to be downleveled to work with ZoneJS. Sometimes libraries in the ecosystem are also incompatible with the way ZoneJS patches the native APIs. Removing ZoneJS as a dependency ensures better long-term compatibility by removing a source of complexity, monkey patching, and ongoing maintenance. 11 | 12 | ## Enabling Zoneless in an application 13 | 14 | The API for enabling Zoneless is currently experimental. Neither the shape, nor the underlying behavior is stable and can change 15 | in patch versions. There are known feature gaps, including the lack of an ergonomic API which prevents the application from serializing too early with Server Side Rendering. 16 | 17 | ```typescript 18 | // standalone bootstrap 19 | bootstrapApplication(MyApp, {providers: [ 20 | provideExperimentalZonelessChangeDetection(), 21 | ]}); 22 | 23 | // NgModule bootstrap 24 | platformBrowser().bootstrapModule(AppModule); 25 | @NgModule({ 26 | providers: [provideExperimentalZonelessChangeDetection()] 27 | }) 28 | export class AppModule {} 29 | ``` 30 | 31 | ## Removing ZoneJS 32 | 33 | Zoneless applications should remove ZoneJS entirely from the build to reduce bundle size. ZoneJS is typically 34 | loaded via the `polyfills` option in `angular.json`, both in the `build` and `test` targets. Remove `zone.js` 35 | and `zone.js/testing` from both to remove it from the build. Projects which use an explicit `polyfills.ts` file 36 | should remove `import 'zone.js';` and `import 'zone.js/testing';` from the file. 37 | 38 | After removing ZoneJS from the build, there is no longer a need for a `zone.js` dependency either and the 39 | package can be removed entirely: 40 | 41 | ```shell 42 | npm uninstall zone.js 43 | ``` 44 | 45 | ## Requirements for Zoneless compatibility 46 | 47 | Angular relies on notifications from core APIs in order to determine when to run change detection and on which views. 48 | These notifications include: 49 | 50 | - `ChangeDetectorRef.markForCheck` (called automatically by `AsyncPipe`) 51 | - `ComponentRef.setInput` 52 | - Updating a signal that's read in a template 53 | - Bound host or template listeners callbacks 54 | - Attaching a view that was marked dirty by one of the above 55 | 56 | ### `OnPush`-compatible components 57 | 58 | One way to ensure that a component is using the correct notification mechanisms from above is to 59 | use [ChangeDetectionStrategy.OnPush](/best-practices/skipping-subtrees#using-onpush). 60 | 61 | The `OnPush` change detection strategy is not required, but it is a recommended step towards zoneless compatibility for application components. It is not always possible for library components to use `ChangeDetectionStrategy.OnPush`. 62 | When a library component is a host for user-components which might use `ChangeDetectionStrategy.Default`, it cannot use `OnPush` because that would prevent the child component from being refreshed if it is not `OnPush` compatible and relies on ZoneJS to trigger change detection. Components can use the `Default` strategy as long as they notify Angular when change detection needs to run (calling `markForCheck`, using signals, `AsyncPipe`, etc.). 63 | Being a host for a user component means using an API such as `ViewContainerRef.createComponent` and not just hosting a portion of a template from a user component (i.e. content projection or a using a template ref input). 64 | 65 | ### Remove `NgZone.onMicrotaskEmpty`, `NgZone.onUnstable`, `NgZone.isStable`, or `NgZone.onStable` 66 | 67 | Applications and libraries need to remove uses of `NgZone.onMicrotaskEmpty`, `NgZone.onUnstable` and `NgZone.onStable`. 68 | These observables will never emit when an Application enables zoneless change detection. 69 | Similarly, `NgZone.isStable` will always be `true` and should not be used as a condition for code execution. 70 | 71 | The `NgZone.onMicrotaskEmpty` and `NgZone.onStable` observables are most often used to wait for Angular to 72 | complete change detection before performing a task. Instead, these can be replaced by `afterNextRender` 73 | if they need to wait for a single change detection or `afterRender` if there is some condition that might span 74 | several change detection rounds. In other cases, these observables were used because they happened to be 75 | familiar and have similar timing to what was needed. More straightforward or direct DOM APIs can be used instead, 76 | such as `MutationObserver` when code needs to wait for certain DOM state (rather than waiting for it indirectly 77 | through Angular's render hooks). 78 | 79 | 80 | `NgZone.run` and `NgZone.runOutsideAngular` do not need to be removed in order for code to be compatible with 81 | Zoneless applications. In fact, removing these calls can lead to performance regressions for libraries that 82 | are used in applications that still rely on ZoneJS. 83 | 84 | 85 | ### `PendingTasks` for Server Side Rendering (SSR) 86 | 87 | If you are using SSR with Angular, you may know that it relies on ZoneJS to help determine when the application 88 | is "stable" and can be serialized. If there are asynchronous tasks that should prevent serialization, an application 89 | not using ZoneJS must make Angular aware of these with the [PendingTasks](/api/core/PendingTasks) service. Serialization 90 | will wait for the first moment that all pending tasks have been removed. 91 | 92 | 93 | The two most straightforward uses of pending tasks are the `run` method: 94 | 95 | ```typescript 96 | const taskService = inject(PendingTasks); 97 | taskService.run(async () => { 98 | const someResult = await doSomeWorkThatNeedsToBeRendered(); 99 | this.someState.set(someResult); 100 | }); 101 | ``` 102 | 103 | For more complicated use-cases, you can manuall add and remove a pending tasks: 104 | 105 | ```typescript 106 | const taskService = inject(PendingTasks); 107 | const taskCleanup = taskService.add(); 108 | try { 109 | await doSomeWorkThatNeedsToBeRendered(); 110 | } catch { 111 | // handle error 112 | } finally { 113 | taskCleanup(); 114 | } 115 | ``` 116 | 117 | In addition, the [pendingUntilEvent](/api/core/rxjs-interop/pendingUntilEvent#) helper in `rxjs-interop` ensures 118 | the application remains unstable until the observable emits, complets, errors, or is unsubscribed. 119 | 120 | ```typescript 121 | readonly myObservableState = someObservable.pipe(pendingUntilEvent()); 122 | ``` 123 | 124 | The framework uses this service internally as well to prevent serialization until asynchronous tasks are complete. These include, but are not limited to, 125 | an ongoing Router navigation and an incomplete `HttpClient` request. 126 | 127 | ## Testing and Debugging 128 | 129 | ### Using Zoneless in `TestBed` 130 | 131 | The zoneless provider function can also be used with `TestBed` to help 132 | ensure the components under test are compatible with a Zoneless 133 | Angular application. 134 | 135 | ```typescript 136 | TestBed.configureTestingModule({ 137 | providers: [provideExperimentalZonelessChangeDetection()] 138 | }); 139 | 140 | const fixture = TestBed.createComponent(MyComponent); 141 | await fixture.whenStable(); 142 | ``` 143 | 144 | To ensure tests have the most similar behavior to production code, 145 | avoid using `fixture.detectChanges()` when possible. This forces 146 | change detection to run when Angular might otherwise have not 147 | scheduled change detection. Tests should ensure these notifications 148 | are happening and allow Angular to handle when to synchronize 149 | state rather than manually forcing it to happen in the test. 150 | 151 | For existing test suites, using `fixture.detectChanges()` is a common pattern 152 | and it is likely not worth the effort of converting these to 153 | `await fixture.whenStable()`. `TestBed` will still enforce that the 154 | fixture's component is `OnPush` compatible and throws `ExpressionChangedAfterItHasBeenCheckedError` 155 | if it finds that template values were updated without a 156 | change notification (i.e. `fixture.componentInstance.someValue = 'newValue';`). 157 | If the component is used in production, this issue should be addressed by updating 158 | the component to use signals for state or call `ChangeDetectorRef.markForCheck()`. 159 | If the component is only used as a test wrapper and never used in an application, 160 | it is acceptable to use `fixture.changeDetectorRef.markForCheck()`. 161 | 162 | ### Debug-mode check to ensure updates are detected 163 | 164 | Angular also provides an additional tool to help verify that an application is making 165 | updates to state in a zoneless-compatible way. `provideExperimentalCheckNoChangesForDebug` 166 | can be used to periodically check to ensure that no bindings have been updated 167 | without a notification. Angular throws `ExpressionChangedAfterItHasBeenCheckedError` 168 | if there is an updated binding that would not have refreshed by the zoneless change 169 | detection. 170 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/elements.md: -------------------------------------------------------------------------------- 1 | # Angular elements overview 2 | 3 | _Angular elements_ are Angular components packaged as _custom elements_ \(also called Web Components\), a web standard for defining new HTML elements in a framework-agnostic way. 4 | 5 | [Custom elements](https://developer.mozilla.org/docs/Web/Web_Components/Using_custom_elements) are a Web Platform feature available on all browsers supported by Angular. 6 | A custom element extends HTML by allowing you to define a tag whose content is created and controlled by JavaScript code. 7 | The browser maintains a `CustomElementRegistry` of defined custom elements, which maps an instantiable JavaScript class to an HTML tag. 8 | 9 | The `@angular/elements` package exports a `createCustomElement()` API that provides a bridge from Angular's component interface and change detection functionality to the built-in DOM API. 10 | 11 | Transforming a component to a custom element makes all the required Angular infrastructure available to the browser. 12 | Creating a custom element is simple and straightforward, and automatically connects your component-defined view with change detection and data binding, mapping Angular functionality to the corresponding built-in HTML equivalents. 13 | 14 | ## Using custom elements 15 | 16 | Custom elements bootstrap themselves - they start when they are added to the DOM, and are destroyed when removed from the DOM. 17 | Once a custom element is added to the DOM for any page, it looks and behaves like any other HTML element, and does not require any special knowledge of Angular terms or usage conventions. 18 | 19 | To add the `@angular/elements` package to your workspace, run the following command: 20 | 21 | 22 | 23 | npm install @angular/elements --save 24 | 25 | 26 | 27 | ### How it works 28 | 29 | The `createCustomElement()` function converts a component into a class that can be registered with the browser as a custom element. 30 | After you register your configured class with the browser's custom-element registry, use the new element just like a built-in HTML element in content that you add directly into the DOM: 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | When your custom element is placed on a page, the browser creates an instance of the registered class and adds it to the DOM. 39 | The content is provided by the component's template, which uses Angular template syntax, and is rendered using the component and DOM data. 40 | Input properties in the component correspond to input attributes for the element. 41 | 42 | ## Transforming components to custom elements 43 | 44 | Angular provides the `createCustomElement()` function for converting an Angular component, together with its dependencies, to a custom element. 45 | 46 | The conversion process implements the `NgElementConstructor` interface, and creates a 47 | constructor class that is configured to produce a self-bootstrapping instance of your component. 48 | 49 | Use the browser's native [`customElements.define()`](https://developer.mozilla.org/docs/Web/API/CustomElementRegistry/define) function to register the configured constructor and its associated custom-element tag with the browser's [`CustomElementRegistry`](https://developer.mozilla.org/docs/Web/API/CustomElementRegistry). 50 | When the browser encounters the tag for the registered element, it uses the constructor to create a custom-element instance. 51 | 52 | IMPORTANT: Avoid using the component's selector as the custom element tag name. 53 | This can lead to unexpected behavior, due to Angular creating two component instances for a single DOM element: 54 | One regular Angular component and a second one using the custom element. 55 | 56 | ### Mapping 57 | 58 | A custom element _hosts_ an Angular component, providing a bridge between the data and logic defined in the component and standard DOM APIs. 59 | Component properties and logic maps directly into HTML attributes and the browser's event system. 60 | 61 | * The creation API parses the component looking for input properties, and defines corresponding attributes for the custom element. 62 | It transforms the property names to make them compatible with custom elements, which do not recognize case distinctions. 63 | The resulting attribute names use dash-separated lowercase. 64 | For example, for a component with `@Input('myInputProp') inputProp`, the corresponding custom element defines an attribute `my-input-prop`. 65 | 66 | * Component outputs are dispatched as HTML [Custom Events](https://developer.mozilla.org/docs/Web/API/CustomEvent), with the name of the custom event matching the output name. 67 | For example, for a component with `@Output() valueChanged = new EventEmitter()`, the corresponding custom element dispatches events with the name "valueChanged", and the emitted data is stored on the event's `detail` property. 68 | If you provide an alias, that value is used; for example, `@Output('myClick') clicks = new EventEmitter();` results in dispatch events with the name "myClick". 69 | 70 | For more information, see Web Component documentation for [Creating custom events](https://developer.mozilla.org/docs/Web/Guide/Events/Creating_and_triggering_events#Creating_custom_events). 71 | 72 | ## Example: A Popup Service 73 | 74 | Previously, when you wanted to add a component to an application at runtime, you had to define a _dynamic component_, and then you would have to load it, attach it to an element in the DOM, and wire up all of the dependencies, change detection, and event handling. 75 | 76 | Using an Angular custom element makes the process simpler and more transparent, by providing all the infrastructure and framework automatically —all you have to do is define the kind of event handling you want. 77 | \(You do still have to exclude the component from compilation, if you are not going to use it in your application.\) 78 | 79 | The following Popup Service example application defines a component that you can either load dynamically or convert to a custom element. 80 | 81 | | Files | Details | 82 | | :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 83 | | `popup.component.ts` | Defines a simple pop-up element that displays an input message, with some animation and styling. | 84 | | `popup.service.ts` | Creates an injectable service that provides two different ways to invoke the `PopupComponent`; as a dynamic component, or as a custom element. Notice how much more setup is required for the dynamic-loading method. | | 85 | | `app.component.ts` | Defines the application's root component, which uses the `PopupService` to add the pop-up to the DOM at run time. When the application runs, the root component's constructor converts `PopupComponent` to a custom element. | 86 | 87 | For comparison, the demo shows both methods. 88 | One button adds the popup using the dynamic-loading method, and the other uses the custom element. 89 | The result is the same, but the preparation is different. 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | ## Typings for custom elements 98 | 99 | Generic DOM APIs, such as `document.createElement()` or `document.querySelector()`, return an element type that is appropriate for the specified arguments. 100 | For example, calling `document.createElement('a')` returns an `HTMLAnchorElement`, which TypeScript knows has an `href` property. 101 | Similarly, `document.createElement('div')` returns an `HTMLDivElement`, which TypeScript knows has no `href` property. 102 | 103 | When called with unknown elements, such as a custom element name \(`popup-element` in our example\), the methods return a generic type, such as `HTMLElement`, because TypeScript can't infer the correct type of the returned element. 104 | 105 | Custom elements created with Angular extend `NgElement` \(which in turn extends `HTMLElement`\). 106 | Additionally, these custom elements will have a property for each input of the corresponding component. 107 | For example, our `popup-element` has a `message` property of type `string`. 108 | 109 | There are a few options if you want to get correct types for your custom elements. 110 | Assume you create a `my-dialog` custom element based on the following component: 111 | 112 | 113 | 114 | @Component(…) 115 | class MyDialog { 116 | @Input() content: string; 117 | } 118 | 119 | 120 | 121 | The most straightforward way to get accurate typings is to cast the return value of the relevant DOM methods to the correct type. 122 | For that, use the `NgElement` and `WithProperties` types \(both exported from `@angular/elements`\): 123 | 124 | 125 | 126 | const aDialog = document.createElement('my-dialog') as NgElement & WithProperties<{content: string}>; 127 | aDialog.content = 'Hello, world!'; 128 | aDialog.content = 123; // <-- ERROR: TypeScript knows this should be a string. 129 | aDialog.body = 'News'; // <-- ERROR: TypeScript knows there is no `body` property on `aDialog`. 130 | 131 | 132 | 133 | This is a good way to quickly get TypeScript features, such as type checking and autocomplete support, for your custom element. 134 | But it can get cumbersome if you need it in several places, because you have to cast the return type on every occurrence. 135 | 136 | An alternative way, that only requires defining each custom element's type once, is augmenting the `HTMLElementTagNameMap`, which TypeScript uses to infer the type of a returned element based on its tag name \(for DOM methods such as `document.createElement()`, `document.querySelector()`, etc.\): 137 | 138 | 139 | 140 | declare global { 141 | interface HTMLElementTagNameMap { 142 | 'my-dialog': NgElement & WithProperties<{content: string}>; 143 | 'my-other-element': NgElement & WithProperties<{foo: 'bar'}>; 144 | … 145 | } 146 | } 147 | 148 | 149 | 150 | Now, TypeScript can infer the correct type the same way it does for built-in elements: 151 | 152 | 153 | 154 | document.createElement('div') //--> HTMLDivElement (built-in element) 155 | document.querySelector('foo') //--> Element (unknown element) 156 | document.createElement('my-dialog') //--> NgElement & WithProperties<{content: string}> (custom element) 157 | document.querySelector('my-other-element') //--> NgElement & WithProperties<{foo: 'bar'}> (custom element) 158 | 159 | 160 | 161 | ## Limitations 162 | 163 | Care should be taken when destroying and then re-attaching custom elements created with `@angular/elements` due to issues with the [disconnect()](https://github.com/angular/angular/issues/38778) callback. Cases where you may run into this issue are: 164 | 165 | - Rendering a component in an `ng-if` or `ng-repeat` in `AngularJs` 166 | - Manually detaching and re-attaching an element to the DOM 167 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/incremental-hydration.md: -------------------------------------------------------------------------------- 1 | # Incremental Hydration 2 | 3 | Tip: Incremental hydration is currently in [developer preview](/reference/releases#developer-preview). 4 | 5 | **Incremental hydration** is an advanced type of [hydration](guide/hydration) that can leave sections of your application dehydrated and _incrementally_ trigger hydration of those sections as they are needed. 6 | 7 | ## Why use incremental hydration? 8 | 9 | Incremental hydration is a performance improvement that builds on top of full application hydration. It can produce smaller initial bundles while still providing an end-user experience that is comparable to a full application hydration experience. Smaller bundles improve initial load times, reducing [First Input Delay (FID)](https://web.dev/fid) and [Cumulative Layout Shift (CLS)](https://web.dev/cls). 10 | 11 | Incremental hydration also lets you use deferrable views (`@defer`) for content that may not have been deferrable before. Specifically, you can now use deferrable views for content that is above the fold. Prior to incremental hydration, putting a `@defer` block above the fold would result in placeholder content rendering and then being replaced by the `@defer` block's main template content. This would result in a layout shift. Incremental hydration means the main template of the `@defer` block will render with no layout shift on hydration. 12 | 13 | ## How do you enable incremental hydration in Angular? 14 | 15 | You can enable incremental hydration for applications that already use server-side rendering (SSR) with hydration. Follow the [Angular SSR Guide](guide/ssr) to enable server-side rendering and the [Angular Hydration Guide](guide/hydration) to enable hydration first. 16 | 17 | Enable incremental hydration by adding the `withIncrementalHydration()` function to the `provideClientHydration` provider. 18 | 19 | ```typescript 20 | import { 21 | bootstrapApplication, 22 | provideClientHydration, 23 | withIncrementalHydration, 24 | } from '@angular/platform-browser'; 25 | ... 26 | 27 | bootstrapApplication(AppComponent, { 28 | providers: [provideClientHydration(withIncrementalHydration())] 29 | }); 30 | ``` 31 | 32 | Incremental Hydration depends on and enables event replay automatically. If you already have `withEventReplay()` in your list, you can safely remove it after enabling incremental hydration. 33 | 34 | ## How does incremental hydration work? 35 | 36 | Incremental hydration builds on top of full-application [hydration](guide/hydration), [deferrable views](guide/defer), and [event replay](guide/hydration#capturing-and-replaying-events). With incremental hydration, you can add additional triggers to `@defer` blocks that define incremental hydration boundaries. Adding a `hydrate` trigger to a defer block tells Angular that it should load that defer block's dependencies during server-side rendering and render the main template rather than the `@placeholder`. When client-side rendering, the dependencies are still deferred, and the defer block content stays dehydrated until its `hydrate` trigger fires. That trigger tells the defer block to fetch its dependencies and hydrate the content. Any browser events, specifically those that match listeners registered in your component, that are triggered by the user prior to hydration are queued up and replayed once the hydration process is complete. 37 | 38 | ## Controlling hydration of content with triggers 39 | 40 | You can specify **hydrate triggers** that control when Angular loads and hydrates deferred content. These are additional triggers that can be used alongside regular `@defer` triggers. 41 | 42 | Each `@defer` block may have multiple hydrate event triggers, separated with a semicolon (`;`). Angular triggers hydration when _any_ of the triggers fire. 43 | 44 | There are three types of hydrate triggers: `hydrate on`, `hydrate when`, and `hydrate never`. 45 | 46 | ### `hydrate on` 47 | 48 | `hydrate on` specifies a condition for when hydration is triggered for the `@defer` block. 49 | 50 | The available triggers are as follows: 51 | 52 | | Trigger | Description | 53 | | --------------------------------------------------- | ---------------------------------------------------------------------- | 54 | | [`hydrate on idle`](#hydrate-on-idle) | Triggers when the browser is idle. | 55 | | [`hydrate on viewport`](#hydrate-on-viewport) | Triggers when specified content enters the viewport | 56 | | [`hydrate on interaction`](#hydrate-on-interaction) | Triggers when the user interacts with specified element | 57 | | [`hydrate on hover`](#hydrate-on-hover) | Triggers when the mouse hovers over specified area | 58 | | [`hydrate on immediate`](#hydrate-on-immediate) | Triggers immediately after non-deferred content has finished rendering | 59 | | [`hydrate on timer`](#hydrate-on-timer) | Triggers after a specific duration | 60 | 61 | #### `hydrate on idle` 62 | 63 | The `hydrate on idle` trigger loads the deferrable view's dependencies and hydrates the content once the browser has reached an idle state, based on `requestIdleCallback`. 64 | 65 | ```angular-html 66 | @defer (hydrate on idle) { 67 | 68 | } @placeholder { 69 |
Large component placeholder
70 | } 71 | ``` 72 | 73 | #### `hydrate on viewport` 74 | 75 | The `hydrate on viewport` trigger loads the deferrable view's dependencies and hydrates the corresponding page of the app when the specified content enters the viewport using the 76 | [Intersection Observer API](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API). 77 | 78 | ```angular-html 79 | @defer (hydrate on viewport) { 80 | 81 | } @placeholder { 82 |
Large component placeholder
83 | } 84 | ``` 85 | 86 | #### `hydrate on interaction` 87 | 88 | The `hydrate on interaction` trigger loads the deferrable view's dependencies and hydrates the content when the user interacts with the specified element through 89 | `click` or `keydown` events. 90 | 91 | ```angular-html 92 | @defer (hydrate on interaction) { 93 | 94 | } @placeholder { 95 |
Large component placeholder
96 | } 97 | ``` 98 | 99 | #### `hydrate on hover` 100 | 101 | The `hydrate on hover` trigger loads the deferrable view's dependencies and hydrates the content when the mouse has hovered over the triggered area through the 102 | `mouseover` and `focusin` events. 103 | 104 | ```angular-html 105 | @defer (hydrate on hover) { 106 | 107 | } @placeholder { 108 |
Large component placeholder
109 | } 110 | ``` 111 | 112 | #### `hydrate on immediate` 113 | 114 | The `hydrate on immediate` trigger loads the deferrable view's dependencies and hydrates the content immediately. This means that the deferred block loads as soon 115 | as all other non-deferred content has finished rendering. 116 | 117 | ```angular-html 118 | @defer (hydrate on immediate) { 119 | 120 | } @placeholder { 121 |
Large component placeholder
122 | } 123 | ``` 124 | 125 | #### `hydrate on timer` 126 | 127 | The `hydrate on timer` trigger loads the deferrable view's dependencies and hydrates the content after a specified duration. 128 | 129 | ```angular-html 130 | @defer (hydrate on timer(500ms)) { 131 | 132 | } @placeholder { 133 |
Large component placeholder
134 | } 135 | ``` 136 | 137 | The duration parameter must be specified in milliseconds (`ms`) or seconds (`s`). 138 | 139 | ### `hydrate when` 140 | 141 | The `hydrate when` trigger accepts a custom conditional expression and loads the deferrable view's dependencies and hydrates the content when the 142 | condition becomes truthy. 143 | 144 | ```angular-html 145 | @defer (hydrate when condition) { 146 | 147 | } @placeholder { 148 |
Large component placeholder
149 | } 150 | ``` 151 | 152 | Note: `hydrate when` conditions only trigger when they are the top-most dehydrated `@defer` block. The condition provided for the trigger is 153 | specified in the parent component, which needs to exist before it can be triggered. If the parent block is dehydrated, that expression will not yet 154 | be resolvable by Angular. 155 | 156 | ### `hydrate never` 157 | 158 | The `hydrate never` allows users to specify that the content in the defer block should remain dehydrated indefinitely, effectively becoming static 159 | content. Note that this applies to the initial render only. During a subsequent client-side render, a `@defer` block with `hydrate never` would 160 | still fetch dependencies, as hydration only applies to initial load of server-side rendered content. In the example below, subsequent client-side 161 | renders would load the `@defer` block dependencies on viewport. 162 | 163 | ```angular-html 164 | @defer (on viewport; hydrate never) { 165 | 166 | } @placeholder { 167 |
Large component placeholder
168 | } 169 | ``` 170 | 171 | Note: Using `hydrate never` prevents hydration of the entire nested subtree of a given `@defer` block. No other `hydrate` triggers fire for content nested underneath that block. 172 | 173 | ## Hydrate triggers alongside regular triggers 174 | 175 | Hydrate triggers are additional triggers that are used alongside regular triggers on a `@defer` block. Hydration is an initial load optimization, and that means hydrate triggers only apply to that initial load. Any subsequent client side render will still use the regular trigger. 176 | 177 | ```angular-html 178 | @defer (on idle; hydrate on interaction) { 179 | 180 | } @placeholder{ 181 |
Example Placeholder
182 | } 183 | ``` 184 | 185 | In this example, on the initial load, the `hydrate on interaction` applies. Hydration will be triggered on interaction with the `` component. On any subsequent page load that is client-side rendered, for example when a user clicks a routerLink that loads a page with this component, the `on idle` will apply. 186 | 187 | ## How does incremental hydration work with nested `@defer` blocks? 188 | 189 | Angular's component and dependency system is hierarchical, which means hydrating any component requires all of its parents also be hydrated. So if hydration is triggered for a child `@defer` block of a nested set of dehydrated `@defer` blocks, hydration is triggered from the top-most dehydrated `@defer` block down to the triggered child and fire in that order. 190 | 191 | ```angular-html 192 | @defer (hydrate on interaction) { 193 | 194 | @defer (hydrate on hover) { 195 | 196 | } @placeholder { 197 |
Child placeholder
198 | } 199 | } @placeholder{ 200 |
Parent Placeholder
201 | } 202 | ``` 203 | 204 | In the above example, hovering over the nested `@defer` block triggers hydration. The parent `@defer` block with the `` hydrates first, then the child `@defer` block with `` hydrates after. 205 | 206 | ## Constraints 207 | 208 | Incremental hydration has the same constraints as full-application hydration, including limits on direct DOM manipulation and requiring valid HTML structure. Visit the [Hydration guide constraints](guide/hydration#constraints) section for more details. 209 | 210 | ## Do I still need to specify `@placeholder` blocks? 211 | 212 | Yes. `@placeholder` block content is not used for incremental hydration, but a `@placeholder` is still necessary for subsequent client-side rendering cases. If your content was not on the route that was part of the initial load, then any navigation to the route that has your `@defer` block content renders like a regular `@defer` block. So the `@placeholder` is rendered in those client-side rendering cases. 213 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/hydration.md: -------------------------------------------------------------------------------- 1 | # Hydration 2 | 3 | ## What is hydration 4 | 5 | Hydration is the process that restores the server-side rendered application on the client. This includes things like reusing the server rendered DOM structures, persisting the application state, transferring application data that was retrieved already by the server, and other processes. 6 | 7 | ## Why is hydration important? 8 | 9 | Hydration improves application performance by avoiding extra work to re-create DOM nodes. Instead, Angular tries to match existing DOM elements to the applications structure at runtime and reuses DOM nodes when possible. This results in a performance improvement that can be measured using [Core Web Vitals (CWV)](https://web.dev/learn-core-web-vitals/) statistics, such as reducing the First Input Delay ([FID](https://web.dev/fid/)) and Largest Contentful Paint ([LCP](https://web.dev/lcp/)), as well as Cumulative Layout Shift ([CLS](https://web.dev/cls/)). Improving these numbers also affects things like SEO performance. 10 | 11 | Without hydration enabled, server-side rendered Angular applications will destroy and re-render the application's DOM, which may result in a visible UI flicker. This re-rendering can negatively impact [Core Web Vitals](https://web.dev/learn-core-web-vitals/) like [LCP](https://web.dev/lcp/) and cause a layout shift. Enabling hydration allows the existing DOM to be re-used and prevents a flicker. 12 | 13 | ## How do you enable hydration in Angular 14 | 15 | Hydration can be enabled for server-side rendered (SSR) applications only. Follow the [Angular SSR Guide](guide/ssr) to enable server-side rendering first. 16 | 17 | ### Using Angular CLI 18 | 19 | If you've used Angular CLI to enable SSR (either by enabling it during application creation or later via `ng add @angular/ssr`), the code that enables hydration should already be included into your application. 20 | 21 | ### Manual setup 22 | 23 | If you have a custom setup and didn't use Angular CLI to enable SSR, you can enable hydration manually by visiting your main application component or module and importing `provideClientHydration` from `@angular/platform-browser`. You'll then add that provider to your app's bootstrapping providers list. 24 | 25 | ```typescript 26 | import { 27 | bootstrapApplication, 28 | provideClientHydration, 29 | } from '@angular/platform-browser'; 30 | ... 31 | 32 | bootstrapApplication(AppComponent, { 33 | providers: [provideClientHydration()] 34 | }); 35 | ``` 36 | 37 | Alternatively if you are using NgModules, you would add `provideClientHydration` to your root app module's provider list. 38 | 39 | ```typescript 40 | import {provideClientHydration} from '@angular/platform-browser'; 41 | import {NgModule} from '@angular/core'; 42 | 43 | @NgModule({ 44 | declarations: [AppComponent], 45 | exports: [AppComponent], 46 | bootstrap: [AppComponent], 47 | providers: [provideClientHydration()], 48 | }) 49 | export class AppModule {} 50 | ``` 51 | 52 | IMPORTANT: Make sure that the `provideClientHydration()` call is also included into a set of providers that is used to bootstrap an application on the **server**. In applications with the default project structure (generated by the `ng new` command), adding a call to the root `AppModule` should be sufficient, since this module is imported by the server module. If you use a custom setup, add the `provideClientHydration()` call to the providers list in the server bootstrap configuration. 53 | 54 | ### Verify that hydration is enabled 55 | 56 | After you've configured hydration and have started up your server, load your application in the browser. 57 | 58 | HELPFUL: You will likely need to fix instances of Direct DOM Manipulation before hydration will fully work either by switching to Angular constructs or by using `ngSkipHydration`. See [Constraints](#constraints), [Direct DOM Manipulation](#direct-dom-manipulation), and [How to skip hydration for particular components](#how-to-skip-hydration-for-particular-components) for more details. 59 | 60 | While running an application in dev mode, you can confirm hydration is enabled by opening the Developer Tools in your browser and viewing the console. You should see a message that includes hydration-related stats, such as the number of components and nodes hydrated. Angular calculates the stats based on all components rendered on a page, including those that come from third-party libraries. 61 | 62 | You can also use [Angular DevTools browser extension](tools/devtools) to see hydration status of components on a page. Angular DevTools also allows to enable an overlay to indicate which parts of the page were hydrated. If there is a hydration mismatch error - DevTools would also highlight a component that caused the error. 63 | 64 | ## Capturing and replaying events 65 | 66 | When an application is rendered on the server, it is visible in a browser as soon as produced HTML loads. Users may assume that they can interact with the page, but event listeners are not attached until hydration completes. Starting from v18, you can enable the Event Replay feature that allows to capture all events that happen before hydration and replay those events once hydration has completed. You can enable it using the `withEventReplay()` function, for example: 67 | 68 | ```typescript 69 | import {provideClientHydration, withEventReplay} from '@angular/platform-browser'; 70 | 71 | bootstrapApplication(App, { 72 | providers: [ 73 | provideClientHydration(withEventReplay()) 74 | ] 75 | }); 76 | ``` 77 | 78 | ### Event Replay 79 | Event Replay is a feature that improves user experience by capturing user events triggered before hydration finishes and replaying them afterward. 80 | 81 | The Event Replay is divided into three main phases: 82 | 83 | - **Capturing user interactions**
84 | Prior to **Hydration**, Event Replay captures and stores all interactions that the user may perform, such as clicks and other browser native events. 85 | 86 | - **Storing events**
87 | The **Event Contract** keeps in memory all the interactions recorded in the previous step, ensuring that they are not lost for later replay. 88 | 89 | - **Relaunch of events**
90 | Once **Hydration** is complete, Angular re-invokes the captured events, ensuring none of those user actions were lost. 91 | 92 | --- 93 | 94 | This feature ensures a consistent user experience, preventing user actions performed before Hydration from being ignored. 95 | 96 | ## Constraints 97 | 98 | Hydration imposes a few constraints on your application that are not present without hydration enabled. Your application must have the same generated DOM structure on both the server and the client. The process of hydration expects the DOM tree to have the same structure in both places. This also includes whitespaces and comment nodes that Angular produces during the rendering on the server. Those whitespaces and nodes must be present in the HTML generated by the server-side rendering process. 99 | 100 | IMPORTANT: The HTML produced by the server side rendering operation **must not** be altered between the server and the client. 101 | 102 | If there is a mismatch between server and client DOM tree structures, the hydration process will encounter problems attempting to match up what was expected to what is actually present in the DOM. Components that do direct DOM manipulation using native DOM APIs are the most common culprit. 103 | 104 | ### Direct DOM Manipulation 105 | 106 | If you have components that manipulate the DOM using native DOM APIs or use `innerHTML` or `outerHTML`, the hydration process will encounter errors. Specific cases where DOM manipulation is a problem are situations like accessing the `document`, querying for specific elements, and injecting additional nodes using `appendChild`. Detaching DOM nodes and moving them to other locations will also result in errors. 107 | 108 | This is because Angular is unaware of these DOM changes and cannot resolve them during the hydration process. Angular will expect a certain structure, but it will encounter a different structure when attempting to hydrate. This mismatch will result in hydration failure and throw a DOM mismatch error ([see below](#errors)). 109 | 110 | It is best to refactor your component to avoid this sort of DOM manipulation. Try to use Angular APIs to do this work, if you can. If you cannot refactor this behavior, use the `ngSkipHydration` attribute ([described below](#how-to-skip-hydration-for-particular-components)) until you can refactor into a hydration friendly solution. 111 | 112 | ### Valid HTML structure 113 | 114 | There are a few cases where if you have a component template that does not have valid HTML structure, this could result in a DOM mismatch error during hydration. 115 | 116 | As an example, here are some of the most common cases of this issue. 117 | 118 | - `` without a `` 119 | - `` element inside tables, modern browsers automatically create a `` element in tables that do not declare one. Because of this inconsistency, always explicitly declare a `` element in tables to avoid hydration errors. 125 | 126 | ### Preserve Whitespaces Configuration 127 | 128 | When using the hydration feature, we recommend using the default setting of `false` for `preserveWhitespaces`. If this setting is not in your tsconfig, the value will be `false` and no changes are required. If you choose to enable preserving whitespaces by adding `preserveWhitespaces: true` to your tsconfig, it is possible you may encounter issues with hydration. This is not yet a fully supported configuration. 129 | 130 | HELPFUL: Make sure that this setting is set **consistently** in `tsconfig.server.json` for your server and `tsconfig.app.json` for your browser builds. A mismatched value will cause hydration to break. 131 | 132 | If you choose to set this setting in your tsconfig, we recommend to set it only in `tsconfig.app.json` which by default the `tsconfig.server.json` will inherit it from. 133 | 134 | ### Custom or Noop Zone.js are not yet supported 135 | 136 | Hydration relies on a signal from Zone.js when it becomes stable inside an application, so that Angular can start the serialization process on the server or post-hydration cleanup on the client to remove DOM nodes that remained unclaimed. 137 | 138 | Providing a custom or a "noop" Zone.js implementation may lead to a different timing of the "stable" event, thus triggering the serialization or the cleanup too early or too late. This is not yet a fully supported configuration and you may need to adjust the timing of the `onStable` event in the custom Zone.js implementation. 139 | 140 | ## Errors 141 | 142 | There are several hydration related errors you may encounter ranging from node mismatches to cases when the `ngSkipHydration` was used on an invalid host node. The most common error case that may occur is due to direct DOM manipulation using native APIs that results in hydration being unable to find or match the expected DOM tree structure on the client that was rendered by the server. The other case you may encounter this type of error was mentioned in the [Valid HTML structure](#valid-html-structure) section earlier. So, make sure the HTML in your templates are using valid structure, and you'll avoid that error case. 143 | 144 | For a full reference on hydration related errors, visit the [Errors Reference Guide](/errors). 145 | 146 | ## How to skip hydration for particular components 147 | 148 | Some components may not work properly with hydration enabled due to some of the aforementioned issues, like [Direct DOM Manipulation](#direct-dom-manipulation). As a workaround, you can add the `ngSkipHydration` attribute to a component's tag in order to skip hydrating the entire component. 149 | 150 | ```angular-html 151 | 152 | ``` 153 | 154 | Alternatively you can set `ngSkipHydration` as a host binding. 155 | 156 | ```typescript 157 | @Component({ 158 | ... 159 | host: {ngSkipHydration: 'true'}, 160 | }) 161 | class ExampleComponent {} 162 | ``` 163 | 164 | The `ngSkipHydration` attribute will force Angular to skip hydrating the entire component and its children. Using this attribute means that the component will behave as if hydration is not enabled, meaning it will destroy and re-render itself. 165 | 166 | HELPFUL: This will fix rendering issues, but it means that for this component (and its children), you don't get the benefits of hydration. You will need to adjust your component's implementation to avoid hydration-breaking patterns (i.e. Direct DOM Manipulation) to be able to remove the skip hydration annotation. 167 | 168 | The `ngSkipHydration` attribute can only be used on component host nodes. Angular throws an error if this attribute is added to other nodes. 169 | 170 | Keep in mind that adding the `ngSkipHydration` attribute to your root application component would effectively disable hydration for your entire application. Be careful and thoughtful about using this attribute. It is intended as a last resort workaround. Components that break hydration should be considered bugs that need to be fixed. 171 | 172 | ## Hydration Timing and Application Stability 173 | 174 | Application stability is an important part of the hydration process. Hydration and any post-hydration processes only occur once the application has reported stability. There are a number of ways that stability can be delayed. Examples include setting timeouts and intervals, unresolved promises, and pending microtasks. In those cases, you may encounter the [Application remains unstable](errors/NG0506) error, which indicates that your app has not yet reached the stable state after 10 seconds. If you're finding that your application is not hydrating right away, take a look at what is impacting application stability and refactor to avoid causing these delays. 175 | 176 | ## I18N 177 | 178 | HELPFUL: Support for internationalization with hydration is currently in [developer preview](/reference/releases#developer-preview). By default, Angular will skip hydration for components that use i18n blocks, effectively re-rendering those components from scratch. 179 | 180 | To enable hydration for i18n blocks, you can add [`withI18nSupport`](/api/platform-browser/withI18nSupport) to your `provideClientHydration` call. 181 | 182 | ```typescript 183 | import { 184 | bootstrapApplication, 185 | provideClientHydration, 186 | withI18nSupport, 187 | } from '@angular/platform-browser'; 188 | ... 189 | 190 | bootstrapApplication(AppComponent, { 191 | providers: [provideClientHydration(withI18nSupport())] 192 | }); 193 | ``` 194 | 195 | ## Consistent rendering across server-side and client-side 196 | Avoid introducing `@if` blocks and other conditionals that display different content when server-side rendering than client-side rendering, such as using an `@if` block with Angular's `isPlatformBrowser` function. These rendering differences cause layout shifts, negatively impacting end-user experience and core web vitals. 197 | 198 | ## Third Party Libraries with DOM Manipulation 199 | 200 | There are a number of third party libraries that depend on DOM manipulation to be able to render. D3 charts is a prime example. These libraries worked without hydration, but they may cause DOM mismatch errors when hydration is enabled. For now, if you encounter DOM mismatch errors using one of these libraries, you can add the `ngSkipHydration` attribute to the component that renders using that library. 201 | 202 | ## Third Party Scripts with DOM Manipulation 203 | 204 | Many third party scripts, such as ad trackers and analytics, modify the DOM before hydration can occur. These scripts may cause hydration errors because the page no longer matches the structure expected by Angular. Prefer deferring this type of script until after hydration whenever possible. Consider using [`AfterNextRender`](api/core/afterNextRender) to delay the script until post-hydration processes have occured. 205 | 206 | ## Incremental Hydration 207 | 208 | Incremental hydration is an advanced form of hydration that allows for more granular control over when hydration happens. See the [incremental hydration guide](guide/incremental-hydration) for more information. 209 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/devtools.md: -------------------------------------------------------------------------------- 1 | # DevTools Overview 2 | 3 | Angular DevTools is a browser extension that provides debugging and profiling capabilities for Angular applications. 4 | 5 | 6 | 7 | Install Angular DevTools from the [Chrome Web Store](https://chrome.google.com/webstore/detail/angular-developer-tools/ienfalfjdbdpebioblfackkekamfmbnh) or from [Firefox Addons](https://addons.mozilla.org/firefox/addon/angular-devtools/). 8 | 9 | You can open Chrome or Firefox DevTools on any web page by pressing F12 or Ctrl+Shift+I (Windows or Linux) and Fn+F12 or Cmd+Option+I (Mac). 10 | Once browser DevTools is open and Angular DevTools is installed, you can find it under the "Angular" tab. 11 | 12 | HELPFUL: Chrome's new tab page does not run installed extensions, so the Angular tab will not appear in DevTools. Visit any other page to see it. 13 | 14 | An overview of Angular DevTools showing a tree of components for an application. 15 | 16 | ## Open your application 17 | 18 | When you open the extension, you'll see two additional tabs: 19 | 20 | | Tabs | Details | 21 | |:--- |:--- | 22 | | [Components](tools/devtools#debug-your-application) | Lets you explore the components and directives in your application and preview or edit their state. | 23 | | [Profiler](tools/devtools#profile-your-application) | Lets you profile your application and understand what the performance bottleneck is during change detection execution. | 24 | 25 | A screenshot of the top of Angular DevTools illustrating two tabs in the upper-left corner, one labeled 'Components' and another labeled 'Profiler'. 26 | 27 | In the top-right corner of Angular DevTools you'll find which version of Angular is running on the page as well as the latest commit hash for the extension. 28 | 29 | ### Angular application not detected 30 | 31 | If you see an error message "Angular application not detected" when opening Angular DevTools, this means it is not able to communicate with an Angular app on the page. 32 | The most common reason for this is because the web page you are inspecting does not contain an Angular application. 33 | Double check that you are inspecting the right web page and that the Angular app is running. 34 | 35 | ### We detected an application built with production configuration 36 | 37 | If you see an error message "We detected an application built with production configuration. Angular DevTools only supports development builds.", this means that an Angular application was found on the page, but it was compiled with production optimizations. 38 | When compiling for production, Angular CLI removes various debug features to minimize the amount of the JavaScript on the page to improve performance. This includes features needed to communicate with DevTools. 39 | 40 | To run DevTools, you need to compile your application with optimizations disabled. `ng serve` does this by default. 41 | If you need to debug a deployed application, disable optimizations in your build with the [`optimization` configuration option](reference/configs/workspace-config#optimization-configuration) (`{"optimization": false}`). 42 | 43 | ## Debug your application 44 | 45 | The **Components** tab lets you explore the structure of your application. 46 | You can visualize the component and directive instances in the DOM and inspect or modify their state. 47 | 48 | ### Explore the application structure 49 | 50 | The component tree displays a hierarchical relationship of the *components and directives* within your application. 51 | 52 | A screenshot of the 'Components' tab showing a tree of Angular components and directives starting the root of the application. 53 | 54 | Click the individual components or directives in the component explorer to select them and preview their properties. 55 | Angular DevTools displays properties and metadata on the right side of the component tree. 56 | 57 | To look up a component or directive by name use the search box above the component tree. 58 | 59 | A screenshot of the 'Components' tab. The filter bar immediately underneath the tab is searching for 'todo' and all components with 'todo' in the name are highlighted in the tree. `app-todos` is currently selected and a sidebar to the right displays information about the component's properties. This includes a section of `@Output` fields and another section for other properties. 60 | 61 | ### Navigate to the host node 62 | 63 | To go to the host element of a particular component or directive, double-click it in the component explorer. 64 | Angular DevTools will open the Elements tab in Chrome or the Inspector tab in Firefox, and select the associated DOM node. 65 | 66 | ### Navigate to source 67 | 68 | For components, Angular DevTools lets you navigate to the component definition in the Sources tab (Chrome) and Debugger tab (Firefox). 69 | After you select a particular component, click the icon at the top-right of the properties view: 70 | 71 | A screenshot of the 'Components' tab. The properties view on the right is visible for a component and the mouse rests in the upper right corner of that view on top of a `<>` icon. An adjacent tooltip reads 'Open component source'. 72 | 73 | ### Update property value 74 | 75 | Like browsers' DevTools, the properties view lets you edit the value of an input, output, or other properties. 76 | Right-click on the property value and if edit functionality is available for this value type, a text input will appear. 77 | Type the new value and press `Enter` to apply this value to the property. 78 | 79 | A screenshot of the 'Components' tab with the properties view open for a component. An `@Input` named `todo` contains a `label` property which is currently selected and has been manually updated to the value 'Buy milk'. 80 | 81 | ### Access selected component or directive in console 82 | 83 | As a shortcut in the console, Angular DevTools provides access to instances of recently selected components or directives. 84 | Type `$ng0` to get a reference to the instance of the currently selected component or directive, and type `$ng1` for the previously selected instance, `$ng2` for the instance selected before that, and so on. 85 | 86 | A screenshot of the 'Components' tab with the browser console underneath. In the console, the user has typed three commands, `$ng0`, `$ng1`, and `$ng2` to view the three most recently selected elements. After each statement, the console prints a different component reference. 87 | 88 | ### Select a directive or component 89 | 90 | Similar to browsers' DevTools, you can inspect the page to select a particular component or directive. 91 | Click the ***Inspect element*** icon in the top left corner within Angular DevTools and hover over a DOM element on the page. 92 | The extension recognizes the associated directives and/or components and lets you select the corresponding element in the Component tree. 93 | 94 | A screenshot of the 'Components' tab with an Angular todo application visible. In the very top-left corner of Angular DevTools, an icon of a screen with a mouse icon inside it is selected. The mouse rests on a todo element in the Angular application UI. The element is highlighted with a `<TodoComponent>` label displayed in an adjacent tooltip. 95 | 96 | ## Profile your application 97 | 98 | The **Profiler** tab lets you visualize the execution of Angular's change detection. 99 | This is useful for determining when and how change detection impacts your application's performance. 100 | 101 | A screenshot of the 'Profiler' tab which reads 'Click the play button to start a new recording, or upload a json file containing profiler data.' Next to this is a record button to being recording a new profile as well as a file picker to select an existing profile. 102 | 103 | The Profiler tab lets you start profiling the current application or import an existing profile from a previous run. 104 | To start profiling your application, hover over the circle in the top-left corner within the **Profiler** tab and click **Start recording**. 105 | 106 | During profiling, Angular DevTools captures execution events, such as change detection and lifecycle hook execution. 107 | Interact with your application to trigger change detection and generate data Angular DevTools can use. 108 | To finish recording, click the circle again to **Stop recording**. 109 | 110 | You can also import an existing recording. 111 | Read more about this feature in the [Import recording](tools/devtools#import-and-export-recordings) section. 112 | 113 | ### Understand your application's execution 114 | 115 | After recording or importing a profile, Angular DevTools displays a visualization of change detection cycles. 116 | 117 | A screenshot of the 'Profiler' tab after a profile has been recorded or uploaded. It displays a bar chart illustrating various change detection cycles with some text which reads 'Select a bar to preview a particular change detection cycle'. 118 | 119 | Each bar in the sequence represents a change detection cycle in your app. 120 | The taller a bar is, the longer the application spent running change detection in this cycle. 121 | When you select a bar, DevTools displays useful information about it including: 122 | 123 | * A bar chart with all the components and directives that it captured during this cycle 124 | * How much time Angular spent running change detection in this cycle. 125 | * An estimated frame rate as experienced by the user. 126 | * The source which triggered change detection. 127 | 128 | A screenshot of the 'Profiler' tab. A single bar has been selected by the user and a nearby dropdown menu displays 'Bar chart`, showing a second bar chart underneath it. The new chart has two bars which take up the majority of the space, one labeled `TodosComponent` and the other labeled `NgForOf`. The other bars are small enough to be negligible in comparison. 129 | 130 | ### Understand component execution 131 | 132 | The bar chart displayed after clicking on a change detection cycle displays a detailed view about how much time your application spent running change detection in that particular component or directive. 133 | 134 | This example shows the total time spent by the `NgForOf` directive and which method was called on it. 135 | 136 | A screenshot of the 'Profiler' tab where the `NgForOf` bar is selected. A detailed view of `NgForOf` is visible to the right where it lists 'Total time spent: 1.76 ms'. It includes a with exactly one row, listing `NgForOf` as a directives with an `ngDoCheck` method which took 1.76 ms. It also includes a list labeled 'Parent Hierarchy' containing the parent components of this directive. 137 | 138 | ### Hierarchical views 139 | 140 | A screenshot of the 'Profiler' tab. A single bar has been selected by the user and a nearby dropdown menu now displays 'Flame graph', showing a flame graph underneath it. The flame graph starts with a row called 'Entire application' and another row called 'AppComponent'. Beneath those, the rows start to break up into multiple items, starting with `[RouterOutlet]` and `DemoAppComponent` on the third row. A few layers deep, one cell is highlighted red. 141 | 142 | You can also visualize the change detection execution in a flame graph-like view. 143 | 144 | Each tile in the graph represents an element on the screen at a specific position in the render tree. 145 | For example, consider a change detection cycle where a `LoggedOutUserComponent` is removed and in its place Angular rendered a `LoggedInUserComponent`. In this scenario both components will be displayed in the same tile. 146 | 147 | The x-axis represents the full time it took to render this change detection cycle. 148 | The y-axis represents the element hierarchy. Running change detection for an element requires render its directives and child components. 149 | Together, this graph visualizes which components are taking the longest time to render and where that time is going. 150 | 151 | Each tile is colored depending on how much time Angular spent there. 152 | Angular DevTools determines the intensity of the color by the time spent relative to the tile where rendering took the most time. 153 | 154 | When you click on a certain tile, you'll see details about it in the panel on the right. 155 | Double-clicking the tile zooms it in so you can more easily view its nested children. 156 | 157 | ### Debug change detection and `OnPush` components 158 | 159 | Normally, the graph visualizes the time it takes to *render* an application, for any given change detection frame. However some components such as `OnPush` components will only re-render if their input properties change. It can be useful to visualize the flame graph without these components for particular frames. 160 | 161 | To visualize only the components in a change detection frame that went through the change detection process, select the **Change detection** checkbox at the top, above the flame graph. 162 | 163 | This view highlights all the components that went through change detection and displays those that did not in gray, such as `OnPush` components that did not re-render. 164 | 165 | A screenshot of the 'Profiler' tab displaying a flame chart visualization of a change detection cycle. A checkbox labeled 'Show only change detection' is now checked. The flame graph looks very similar to before, however the color of components has changed from orange to blue. Several tiles labeled `[RouterOutlet]` are no longer highlighted with any color. 166 | 167 | ### Import and export recordings 168 | 169 | Click the **Save Profile** button at the top-right of a recorded profiling session to export it as a JSON file and save it to the disk. 170 | Later, import the file in the initial view of the profiler by clicking the **Choose file** input. 171 | 172 | A screenshot of the 'Profiler' tab displaying change detection cycles. On the right side a 'Save Profile' button is visible. 173 | 174 | ## Inspect your injectors 175 | 176 | Note: The Injector Tree is available for Angular Applications built with version 17 or higher. 177 | 178 | ### View the injector hierarchy of your application 179 | 180 | The **Injector Tree** tab lets you explore the structure of the Injectors configured in your application. Here you will see two trees representing the [injector hierarchy](guide/di/hierarchical-dependency-injection) of your application. One tree is your environment hierarchy, the other is your element hierarchy. 181 | 182 | A screenshot of the 'Profiler' tab displaying the injector tree tab in Angular Devtools visualizing the injector graph for an example application. 183 | 184 | ### Visualize resolution paths 185 | 186 | When a specific injector is selected, the path that Angular's dependency injection algorithm traverses from that injector to the root is highlighted. For element injectors, this includes highlighting the environment injectors that the dependency injection algorithm jumps to when a dependency cannot be resolved in the element hierarchy. 187 | 188 | See [resolution rules](guide/di/hierarchical-dependency-injection#resolution-rules) for more details about how Angular resolves resolution paths. 189 | 190 | A screenshot of the 'Profiler' tab displaying how the injector tree visualize highlights resolution paths when an injector is selected. 191 | 192 | ### View injector providers 193 | 194 | Clicking an injector that has configured providers will display those providers in a list on the right of the injector tree view. Here you can view the provided token and it's type. 195 | 196 | A screenshot of the 'Profiler' tab displaying how providers are made visible when an injector is selected. 197 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/guide-pipes.md: -------------------------------------------------------------------------------- 1 | # Guide Pipes 2 | 3 | (From change-detection.md) 4 | 5 | ## Change detection with pipes 6 | 7 | Pipes are often used with data-bound values that might change based on user actions. 8 | If the data is a primitive input value, such as `String` or `Number`, or an object reference as input, such as `Date` or `Array`, Angular executes the pipe whenever it detects a change for the value. 9 | 10 | 11 | 13 | 14 | 15 | 16 | The `exponentialStrength` pipe executes every time the user changes the value or the exponent. See the highlighted line above. 17 | 18 | Angular detects each change and immediately runs the pipe. 19 | This is fine for primitive input values. 20 | However, if you change something *inside* a composite object (such as the month of a date, an element of an array, or an object property), you need to understand how change detection works, and how to use an `impure` pipe. 21 | 22 | ### How change detection works 23 | 24 | Angular looks for changes to data-bound values in a change detection process that runs after every DOM event: every keystroke, mouse move, timer tick, and server response. 25 | The following example, which doesn't use a pipe, demonstrates how Angular uses its default change detection strategy to monitor and update its display of every hero in the `heroes` array. 26 | The example tabs show the following: 27 | 28 | | Files | Details | 29 | |:--- |:--- | 30 | | `flying-heroes.component.html (v1)` | The `*ngFor` repeater displays the hero names. | 31 | | `flying-heroes.component.ts (v1)` | Provides heroes, adds heroes into the array, and resets the array. | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Angular updates the display every time the user adds a hero. 39 | If the user clicks the **Reset** button, Angular replaces `heroes` with a new array of the original heroes and updates the display. 40 | If you add the ability to remove or change a hero, Angular would detect those changes and update the display as well. 41 | 42 | However, executing a pipe to update the display with every change would slow down your application's performance. 43 | So Angular uses a faster change-detection algorithm for executing a pipe, as described in the next section. 44 | 45 | ### Detecting pure changes to primitives and object references 46 | 47 | By default, pipes are defined as *pure* so that Angular executes the pipe only when it detects a *pure change* to the input value or parameters. 48 | A pure change is either a change to a primitive input value \(such as `String`, `Number`, `Boolean`, or `Symbol`\), or a changed object reference \(such as `Date`, `Array`, `Function`, or `Object`\). 49 | 50 | A pure pipe must use a pure function, which is one that processes inputs and returns values without side effects. 51 | In other words, given the same input, a pure function should always return the same output. 52 | 53 | With a pure pipe, Angular ignores changes within objects and arrays because checking a primitive value or object reference is much faster than performing a deep check for differences within objects. 54 | Angular can quickly determine if it can skip executing the pipe and updating the view. 55 | 56 | However, a pure pipe with an array as input might not work the way you want. 57 | To demonstrate this issue, change the previous example to filter the list of heroes to just those heroes who can fly. 58 | 59 | For this, consider we use the `FlyingHeroesPipe` in the `*ngFor` repeater as shown in the following code. 60 | The tabs for the example show the following: 61 | 62 | | Files | Details | 63 | |:--- |:--- | 64 | | flying-heroes.component.html | Template with the new pipe used. | 65 | | flying-heroes.pipe.ts | File with custom pipe that filters flying heroes. | 66 | 67 | 68 | 69 | 70 | 71 | 72 | The application now shows unexpected behavior: When the user adds flying heroes, none of them appear under "Heroes who fly." 73 | This happens because the code that adds a hero does so by pushing it onto the `heroes` array that is used as input for the `flyingHeroes` pipe. 74 | 75 | 76 | 77 | The change detector ignores changes within elements of an array, so the pipe doesn't run. 78 | The reason Angular ignores the changed array element is that the *reference* to the array hasn't changed. 79 | Because the array is the same, Angular does not update the display. 80 | 81 | One way to get the behavior you want is to change the object reference itself. 82 | Replace the array with a new array containing the newly changed elements, and then input the new array to the pipe. 83 | In the preceding example, create an array with the new hero appended, and assign that to `heroes`. 84 | Angular detects the change in the array reference and executes the pipe. 85 | 86 | To summarize, if you mutate the input array, the pure pipe doesn't execute. 87 | If you *replace* the input array, the pipe executes and the display is updated. 88 | As an alternative, use an *impure* pipe to detect changes within composite objects such as arrays, as described in the next section. 89 | 90 | ### Detecting impure changes within composite objects 91 | 92 | To execute a custom pipe after a change *within* a composite object, such as a change to an element of an array, you need to define your pipe as `impure` to detect impure changes. 93 | Angular executes an impure pipe every time it detects a change (e.g. every keystroke or mouse event). 94 | 95 | IMPORTANT: While an impure pipe can be useful, be careful using one. 96 | A long-running impure pipe could dramatically slow down your application. 97 | 98 | Make a pipe impure by setting its `pure` flag to `false`: 99 | 100 | 102 | 103 | The following code shows the complete implementation of `FlyingHeroesImpurePipe`, which extends `FlyingHeroesPipe` to inherit its characteristics. 104 | The example shows that you don't have to change anything else—the only difference is setting the `pure` flag as `false` in the pipe metadata. 105 | 106 | 107 | 108 | 109 | 110 | 111 | `FlyingHeroesImpurePipe` is a reasonable candidate for an impure pipe because the `transform` function is trivial and fast: 112 | 113 | 114 | 115 | You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`. 116 | As shown in the following code, only the pipe in the template changes. 117 | 118 | 119 | 120 | --- 121 | 122 | 123 | (From overview.md) 124 | 125 | ## Understanding Pipes 126 | 127 | Use pipes to transform strings, currency amounts, dates, and other data for display. 128 | 129 | ### What is a pipe 130 | 131 | Pipes are simple functions to use in templates to accept an input value and return a transformed value. Pipes are useful because you can use them throughout your application, while only declaring each pipe once. 132 | For example, you would use a pipe to show a date as **April 15, 1988** rather than the raw string format. 133 | 134 | You can create your own custom pipes to expose reusable transformations in templates. 135 | 136 | ### Built-in pipes 137 | 138 | Angular provides built-in pipes for typical data transformations, including transformations for internationalization (i18n), which use locale information to format data. 139 | The following are commonly used built-in pipes for data formatting: 140 | 141 | - [`DatePipe`](api/common/DatePipe): Formats a date value according to locale rules. 142 | - [`UpperCasePipe`](api/common/UpperCasePipe): Transforms text to all upper case. 143 | - [`LowerCasePipe`](api/common/LowerCasePipe): Transforms text to all lower case. 144 | - [`CurrencyPipe`](api/common/CurrencyPipe): Transforms a number to a currency string, formatted according to locale rules. 145 | - [`DecimalPipe`](/api/common/DecimalPipe): Transforms a number into a string with a decimal point, formatted according to locale rules. 146 | - [`PercentPipe`](api/common/PercentPipe): Transforms a number to a percentage string, formatted according to locale rules. 147 | - [`AsyncPipe`](api/common/AsyncPipe): Subscribe and unsubscribe to an asynchronous source such as an observable. 148 | - [`JsonPipe`](api/common/JsonPipe): Display a component object property to the screen as JSON for debugging. 149 | 150 | Note: For a complete list of built-in pipes, see the [pipes API documentation](/api?type=pipe "Pipes API reference summary"). 151 | To learn more about using pipes for internationalization (i18n) efforts, see [formatting data based on locale](guide/i18n/format-data-locale). 152 | 153 | --- 154 | 155 | 156 | (From precedence.md) 157 | 158 | ## Pipe precedence in template expressions 159 | 160 | Sometimes you want to choose between two values, based on some condition, before passing the choice to the pipe. 161 | You could use the JavaScript ternary operator (`?:`) in the template to make that choice. 162 | 163 | Beware! The pipe operator has a higher precedence than the JavaScript ternary operator (`?:`). 164 | 165 | If you simply write the expression as if it were evaluated left-to-right, you might be surprised by the result. For example, 166 | 167 | ```ts 168 | condition ? a : b | pipe 169 | ``` 170 | 171 | is parsed as 172 | 173 | ```ts 174 | condition ? a : (b | pipe) 175 | ``` 176 | 177 | The value of `b` passes through `pipe`; the value of `a` *will not*. 178 | 179 | If you want the pipe to apply to the result of the ternary expression, wrap the entire expression in parentheses. For example, 180 | 181 | ```ts 182 | (condition ? a : b) | pipe 183 | ``` 184 | 185 | In general, you should always use parentheses to be sure Angular evaluates the expression as you intend. 186 | 187 | --- 188 | 189 | 190 | (From template.md) 191 | 192 | ## Using a pipe in a template 193 | 194 | To apply a pipe, use the pipe operator (`|`) within a template expression as shown in the following code example. 195 | 196 | 197 |

The hero's birthday is {{ birthday | date }}

198 |
199 | 200 | The component's `birthday` value flows through the pipe operator (`|`) to the [`DatePipe`](api/common/DatePipe) whose pipe name is `date`. 201 | The pipe renders the date in the default format like **Apr 07, 2023**. 202 | 203 | 204 | import { Component } from '@angular/core'; 205 | import { DatePipe } from '@angular/common'; 206 | 207 | @Component({ 208 | templateUrl: './app.component.html', 209 | imports: [DatePipe], 210 | }) 211 | export class AppComponent { 212 | birthday = new Date(); 213 | } 214 | 215 | 216 | ### Additional parameters for pipes 217 | 218 | Pipes can take additional parameters that configure the transformation. Parameters can be optional or required. 219 | 220 | For example, the `date` pipe takes optional parameters that control the date's display format. 221 | To specify the parameter, follow the pipe name with a colon (`:`) and the parameter value (the format). 222 | 223 | 224 |

The hero's birthday is in {{ birthday | date:'yyyy' }}

225 |
226 | 227 | Pipes can also take multiple parameters. You can pass multiple parameters by separating these via colons (`:`). 228 | For example, the `date` pipe accepts a second optional parameter for controlling the timezone. 229 | 230 | 231 |

The current time is: {{ currentTime | date:'hh:mm':'UTC' }}

232 |
233 | 234 | This will display the current time (like `10:53`) in the `UTC` timezone. 235 | 236 | ### Chaining pipes 237 | 238 | You can connect multiple pipes so that the output of one pipe becomes the input to the next. 239 | 240 | The following example passes a date to the `DatePipe` and then forwards the result to the [`UpperCasePipe`](api/common/UpperCasePipe 'API reference') pipe. 241 | 242 | 243 |

The hero's birthday is {{ birthday | date }}

244 |

The hero's birthday is in {{ birthday | date:'yyyy' | uppercase }}

245 |
246 | 247 | --- 248 | 249 | 250 | (From transform-data.md) 251 | 252 | ## Custom pipes for new transforms 253 | 254 | Create custom pipes to encapsulate transformations that are not provided with the built-in pipes. 255 | Then, use your custom pipe in template expressions, the same way you use built-in pipes—to transform input values to output values for display. 256 | 257 | ### Marking a class as a pipe 258 | 259 | To mark a class as a pipe and supply configuration metadata, apply the `@Pipe` to the class. 260 | 261 | Use UpperCamelCase (the general convention for class names) for the pipe class name, and camelCase for the corresponding `name` string. 262 | Do not use hyphens in the `name`. 263 | 264 | For details and more examples, see [Pipe names](/style-guide#pipe-names "Pipe names in the Angular coding style guide"). 265 | 266 | Use `name` in template expressions as you would for a built-in pipe. 267 | 268 | ```ts 269 | import { Pipe } from '@angular/core'; 270 | 271 | @Pipe({ 272 | name: 'greet', 273 | }) 274 | export class GreetPipe {} 275 | ``` 276 | 277 | ### Using the PipeTransform interface 278 | 279 | Implement the [`PipeTransform`](/api/core/PipeTransform "API reference for PipeTransform") interface in your custom pipe class to perform the transformation. 280 | 281 | Angular invokes the `transform` method with the value of a binding as the first argument, and any parameters as the second argument in list form, and returns the transformed value. 282 | 283 | ```ts 284 | import { Pipe, PipeTransform } from '@angular/core'; 285 | 286 | @Pipe({ 287 | name: 'greet', 288 | }) 289 | export class GreetPipe implements PipeTransform { 290 | transform(value: string, param1: boolean, param2: boolean): string { 291 | return `Hello ${value}`; 292 | } 293 | } 294 | ``` 295 | 296 | ### Example: Transforming a value exponentially 297 | 298 | In a game, you might want to implement a transformation that raises a value exponentially to increase a hero's power. 299 | For example, if the hero's score is 2, boosting the hero's power exponentially by 10 produces a score of 1024 (`2**10`). 300 | Use a custom pipe for this transformation. 301 | 302 | The following code example shows two component definitions: 303 | 304 | | Files | Details | 305 | |:--- |:--- | 306 | | `exponential-strength.pipe.ts` | Defines a custom pipe named `exponentialStrength` with the `transform` method that performs the transformation. It defines an argument to the `transform` method \(`exponent`\) for a parameter passed to the pipe. | 307 | | `power-booster.component.ts` | Demonstrates how to use the pipe, specifying a value \(`2`\) and the exponent parameter \(`10`\). | 308 | 309 | 310 | 311 | 312 | 313 | 314 | --- 315 | 316 | 317 | (From unwrapping-data-observables.md) 318 | 319 | ## Unwrapping data from an observable 320 | 321 | Observables let you pass messages between parts of your application. 322 | You can use observables for event handling, asynchronous programming, and handling multiple values. 323 | Observables can deliver single or multiple values of any type, either synchronously (as a function delivers a value to its caller) or asynchronously on a schedule. 324 | 325 | Use the built-in [`AsyncPipe`](api/common/AsyncPipe "API description of AsyncPipe") to accept an observable as input and subscribe to the input automatically. 326 | Without this pipe, your component code would have to subscribe to the observable to consume its values, extract the resolved values, expose them for binding, and unsubscribe when the observable is destroyed in order to prevent memory leaks. 327 | `AsyncPipe` is a pipe that saves boilerplate code in your component to maintain the subscription and keep delivering values from that observable as they arrive. 328 | 329 | The following code example binds an observable of message strings (`message$`) to a view with the `async` pipe. 330 | 331 | 332 | 334 | 335 | --- -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/hybrid-rendering.md: -------------------------------------------------------------------------------- 1 | # Hybrid rendering 2 | 3 | ## What is hybrid rendering? 4 | 5 | Hybrid rendering combines the benefits of server-side rendering (SSR), pre-rendering (also known as "static site generation" or SSG) and client-side rendering (CSR) to optimize your Angular application. It allows you to render different parts of your application using different strategies, giving you fine-grained control over how your app is delivered to users. 6 | 7 | Angular’s new **developer preview** server rendering APIs offer a more efficient and adaptable approach to building modern web applications. These APIs give you complete control over your app’s rendering, allowing for optimizations that enhance performance, Search Engine Optimization (SEO), and overall user experience. 8 | 9 | **Benefits of these new APIs:** 10 | 11 | - **Greater flexibility:** 12 | - Leverage fine-grained control over rendering allows you to optimize for performance and user experience in different parts of your application. 13 | - Choose the best rendering strategy for each route, whether it's server-side rendering for fast initial load times, client-side rendering for dynamic interactivity, or a hybrid approach. 14 | - **Built-in internationalization (i18n):** 15 | - Easily adapt your application to different languages and regions with out-of-the-box i18n support. 16 | - **Environment agnostic:** 17 | - Use these APIs with any JavaScript runtime environment, not just Node.js. 18 | - Enjoy the benefits of enhanced rendering capabilities regardless of your technology stack. 19 | - **Seamless dev server integration:** 20 | - Take advantage of a smooth and efficient development experience from a fully integrated development server. 21 | 22 | This developer preview gives you a first look at these powerful new features. The Angular team encourages you to explore them and provide feedback to help shape the future of Angular server rendering. 23 | 24 | ## Setting up hybrid rendering 25 | 26 | You can create a **new** project with server-side routing with the Angular CLI: 27 | 28 | ```shell 29 | ng new --ssr --server-routing 30 | ``` 31 | 32 | You can also add server-side routing to an existing project with the `ng add` command: 33 | 34 | ```shell 35 | ng add @angular/ssr --server-routing 36 | ``` 37 | 38 | ## Server routing 39 | 40 | ### Configuring server routes 41 | 42 | You can create a server route config by declaring an array of [`ServerRoute`](api/ssr/ServerRoute 'API reference') objects. This configuration typically lives in a file named `app.routes.server.ts`. 43 | 44 | ```typescript 45 | // app.routes.server.ts 46 | import { RenderMode, ServerRoute } from '@angular/ssr'; 47 | 48 | export const serverRoutes: ServerRoute[] = [ 49 | { 50 | path: '', // This renders the "/" route on the client (CSR) 51 | renderMode: RenderMode.Client, 52 | }, 53 | { 54 | path: 'about', // This page is static, so we prerender it (SSG) 55 | renderMode: RenderMode.Prerender, 56 | }, 57 | { 58 | path: 'profile', // This page requires user-specific data, so we use SSR 59 | renderMode: RenderMode.Server, 60 | }, 61 | { 62 | path: '**', // All other routes will be rendered on the server (SSR) 63 | renderMode: RenderMode.Server, 64 | }, 65 | ]; 66 | ``` 67 | 68 | You can add this config to your application using the [`provideServerRouting`](api/ssr/provideServerRouting 'API reference') function. 69 | 70 | ```typescript 71 | import { provideServerRouting } from '@angular/ssr'; 72 | import { serverRoutes } from './app.routes.server'; 73 | 74 | // app.config.server.ts 75 | const serverConfig: ApplicationConfig = { 76 | providers: [ 77 | provideServerRendering(), 78 | provideServerRouting(serverRoutes), 79 | // ... other providers ... 80 | ] 81 | }; 82 | ``` 83 | 84 | When using the [App shell pattern](ecosystem/service-workers/app-shell), you must specify the route to be used as the app shell for client-side rendered routes. To do this, provide an options object with the `appShellRoute` property to [`provideServerRouting`](api/ssr/provideServerRouting 'API reference'): 85 | 86 | ```typescript 87 | import { provideServerRouting, withAppShell } from '@angular/ssr'; 88 | import { AppShellComponent } from './app-shell/app-shell.component'; 89 | 90 | const serverConfig: ApplicationConfig = { 91 | providers: [ 92 | provideServerRouting(serverRoutes, withAppShell(AppShellComponent)), 93 | // ... other providers ... 94 | ] 95 | }; 96 | ``` 97 | 98 | ### Rendering modes 99 | 100 | The server routing configuration lets you specify how each route in your application should render by setting a [`RenderMode`](api/ssr/RenderMode 'API reference'): 101 | 102 | | Rendering mode | Description | 103 | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 104 | | **Server (SSR)** | Renders the application on the server for each request, sending a fully populated HTML page to the browser. See the [Server-Side Rendering (SSR) guide](guide/ssr) for more information. | 105 | | **Client (CSR)** | Renders the application in the browser. This is the default Angular behavior. | 106 | | **Prerender (SSG)** | Prerenders the application at build time, generating static HTML files for each route. See the [Prerendering guide](guide/prerendering) for more information. | 107 | 108 | #### Choosing a rendering mode 109 | 110 | Each rendering mode has different benefits and drawbacks. You can choose rendering modes based on the specific needs of your application. 111 | 112 | ##### Client-side rendering 113 | 114 | Client-side rendering has the simplest development model, as you can write code that assumes it always runs in a web browser. This lets you use a wide range of client-side libraries that also assume they run in a browser. 115 | 116 | Client-side rendering generally has worse performance than other rendering modes, as it must download, parse, and execute your page's JavaScript before the user can see any rendered content. If your page fetches more data from the server as it renders, users also have to wait for those additional requests before they can view the complete content. 117 | 118 | If your page is indexed by search crawlers, client-side rendering may negatively affect search engine optimization (SEO), as search crawlers have limits to how much JavaScript they execute when indexing a page. 119 | 120 | When client-side rendering, the server does not need to do any work to render a page beyond serving static JavaScript assets. You may consider this factor if server cost is a concern. 121 | 122 | Applications that support installable, offline experiences with [service workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can rely on client-side rendering without needing to communicate with a server. 123 | 124 | ##### Server-side rendering 125 | 126 | Server-side rendering offers faster page loads than client-side rendering. Instead of waiting for JavaScript to download and run, the server directly renders an HTML document upon receiving a request from the browser. The user experiences only the latency necessary for the server to fetch data and render the requested page. This mode also eliminates the need for additional network requests from the browser, as your code can fetch data during rendering on the server. 127 | 128 | Server-side rendering generally has excellent search engine optimization (SEO), as search crawlers receive a fully rendered HTML document. 129 | 130 | Server-side rendering requires you to author code that does not strictly depend on browser APIs and limits your selection of JavaScript libraries that assume they run in a browser. 131 | 132 | When server-side rendering, your server runs Angular to produce an HTML response for every request. This additional cost may affect server hosting costs. 133 | 134 | ##### Build-time prerendering 135 | 136 | Prerendering offers faster page loads than both client-side rendering and server-side rendering. Because prerendering creates HTML documents at _build-time_, the server can directly respond to requests with the static HTML document without any additional work. 137 | 138 | Prerendering requires that all information necessary to render a page is available at _build-time_. This means that prerendered pages cannot include any data to the specific user loading the page. This means that prerendering is primarily useful for pages that are the same for all users of your application. 139 | 140 | Because prerendering occurs at build-time, it may add significant time to your production builds. Using [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') to produce a large number of HTML documents may affect the total file size of your deployments, and thus lead to slower deployments. 141 | 142 | It may also add time to your deployments based on the number of static HTML documents included in your build output. 143 | 144 | Prerendering generally has excellent search engine optimization (SEO), as search crawlers receive a fully rendered HTML document. 145 | 146 | Prerendering requires you to author code that does not strictly depend on browser APIs and limits your selection of JavaScript libraries that assume they run in a browser. 147 | 148 | Prerendering incurs extremely little overhead per server request, as your server responds with static HTML documents. Static files are also easily cached by Content Delivery Networks (CDNs), browsers, and intermediate caching layers for even faster subsequent page loads. Deploying static HTML files to a CDN improves scalability by offloading work from your application web server, which is impactful for high-traffic applications. 149 | 150 | ### Setting headers and status codes 151 | 152 | You can set custom headers and status codes for individual server routes using the `headers` and `status` properties in the `ServerRoute` configuration. 153 | 154 | ```typescript 155 | // app.routes.server.ts 156 | import { RenderMode, ServerRoute } from '@angular/ssr'; 157 | 158 | export const serverRoutes: ServerRoute[] = [ 159 | { 160 | path: 'profile', 161 | renderMode: RenderMode.Server, 162 | headers: { 163 | 'X-My-Custom-Header': 'some-value', 164 | }, 165 | status: 201, 166 | }, 167 | // ... other routes 168 | ]; 169 | ``` 170 | 171 | ### Redirects 172 | 173 | Angular handles redirects specified by the [`redirectTo`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') property in route configurations, differently on the server-side. 174 | 175 | **Server-Side Rendering (SSR)** 176 | Redirects are performed using standard HTTP redirects (e.g., 301, 302) within the server-side rendering process. 177 | 178 | **Prerendering (SSG)** 179 | Redirects are implemented as "soft redirects" using `` tags in the prerendered HTML. This allows for redirects without requiring a round trip to the server. 180 | 181 | ### Customizing build-time prerendering (SSG) 182 | 183 | When using [`RenderMode.Prerender`](api/ssr/RenderMode#Prerender 'API reference'), you can specify several configuration options to customize the prerendering and serving process. 184 | 185 | #### Parameterized routes 186 | 187 | For each route with [`RenderMode.Prerender`](api/ssr/RenderMode#Prerender 'API reference'), you can specify a [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') function. This function lets you control which specific parameters produce separate prerendered documents. 188 | 189 | The [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') function returns a `Promise` that resolves to an array of objects. Each object is a key-value map of route parameter name to value. For example, if you define a route like `posts/:id`, `getPrerenderParams ` could return the array `[{id: 123}, {id: 456}]`, and thus render separate documents for `posts/123` and `posts/456`. 190 | 191 | The body of [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') can use Angular's [`inject`](api/core/inject 'API reference') function to inject dependencies and perform any work to determine which routes to prerender. This typically includes making requests to fetch data to construct the array of parameter values. 192 | 193 | ```typescript 194 | // app.routes.server.ts 195 | import { RenderMode, ServerRoute } from '@angular/ssr'; 196 | 197 | export const serverRoutes: ServerRoute[] = [ 198 | { 199 | path: 'post/:id', 200 | renderMode: RenderMode.Prerender, 201 | async getPrerenderParams() { 202 | const dataService = inject(PostService); 203 | const ids = await dataService.getIds(); // Assuming this returns ['1', '2', '3'] 204 | 205 | return ids.map(id => ({ id })); // Transforms IDs into an array of objects for prerendering 206 | 207 | // This will prerender the paths: `/post/1`, `/post/2` and `/post/3` 208 | }, 209 | }, 210 | ]; 211 | ``` 212 | 213 | Because [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') exclusively applies to [`RenderMode.Prerender`](api/ssr/RenderMode#Prerender 'API reference'), this function always runs at _build-time_. `getPrerenderParams` must not rely on any browser-specific or server-specific APIs for data. If the route does not specify a [`fallback`](api/ssr/ServerRoutePrerenderWithParams#fallback 'API reference') option, the route falls back to [`PrerenderFallback.Server`](api/ssr/PrerenderFallback#Server 'API reference') (SSR) by default. 214 | 215 | IMPORTANT: When using [`inject`](api/core/inject 'API reference') inside `getPrerenderParams`, please remember that `inject` must be used synchronously. It cannot be invoked within asynchronous callbacks or following any `await` statements. For more information, refer to [`runInInjectionContext` API reference](api/core/runInInjectionContext). 216 | 217 | #### Fallback strategies 218 | 219 | When using [`RenderMode.Prerender`](api/ssr/RenderMode#Prerender 'API reference') mode, you can specify a fallback strategy to handle requests for paths that haven't been prerendered. 220 | 221 | The available fallback strategies are: 222 | 223 | - **Server:** Fallback to server-side rendering. This is the **default** behavior if no `fallback` property is specified. 224 | - **Client:** Fallback to client-side rendering. 225 | - **None:** No fallback. Angular will not handle requests for paths that are not prerendered. 226 | 227 | ```typescript 228 | // app.routes.server.ts 229 | import { RenderMode, PrerenderFallback, ServerRoute } from '@angular/ssr'; 230 | 231 | export const serverRoutes: ServerRoute[] = [ 232 | { 233 | path: 'post/:id', 234 | renderMode: RenderMode.Prerender, 235 | fallback: PrerenderFallback.Client, // Fallback to CSR if not prerendered 236 | async getPrerenderParams() { 237 | // This function returns an array of objects representing prerendered posts at the paths: 238 | // `/post/1`, `/post/2`, and `/post/3`. 239 | // The path `/post/4` will utilize the fallback behavior if it's requested. 240 | return [{ id: 1 }, { id: 2 }, { id: 3 }]; 241 | }, 242 | }, 243 | ]; 244 | ``` 245 | 246 | ## Accessing Request and Response via DI 247 | 248 | The `@angular/core` package provides several tokens for interacting with the server-side rendering environment. These tokens give you access to crucial information and objects within your Angular application during SSR. 249 | 250 | - **[`REQUEST`](api/core/REQUEST 'API reference'):** Provides access to the current request object, which is of type [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) from the Web API. This allows you to access headers, cookies, and other request information. 251 | - **[`RESPONSE_INIT`](api/core/RESPONSE_INIT 'API reference'):** Provides access to the response initialization options, which is of type [`ResponseInit`](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#parameters) from the Web API. This allows you to set headers and the status code for the response dynamically. Use this token to set headers or status codes that need to be determined at runtime. 252 | - **[`REQUEST_CONTEXT`](api/core/REQUEST_CONTEXT 'API reference'):** Provides access to additional context related to the current request. This context can be passed as the second parameter of the [`handle`](api/ssr/AngularAppEngine#handle 'API reference') function. Typically, this is used to provide additional request-related information that is not part of the standard Web API. 253 | 254 | ```angular-ts 255 | import { inject, REQUEST } from '@angular/core'; 256 | 257 | @Component({ 258 | selector: 'app-my-component', 259 | template: `

My Component

`, 260 | }) 261 | export class MyComponent { 262 | constructor() { 263 | const request = inject(REQUEST); 264 | console.log(request?.url); 265 | } 266 | } 267 | ``` 268 | 269 | IMPORTANT: The above tokens will be `null` in the following scenarios: 270 | - During the build processes. 271 | - When the application is rendered in the browser (client-side rendering). 272 | - When performing static site generation (SSG). 273 | - During route extraction in development (at the time of the request). 274 | 275 | ## Configuring a non-Node.js Server 276 | 277 | The `@angular/ssr` provides essential APIs for server-side rendering your Angular application on platforms other than Node.js. It leverages the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects from the Web API, enabling you to integrate Angular SSR into various server environments. For detailed information and examples, refer to the [`@angular/ssr` API reference](api/ssr/AngularAppEngine). 278 | 279 | ```typescript 280 | // server.ts 281 | import { AngularAppEngine, createRequestHandler } from '@angular/ssr'; 282 | 283 | const angularApp = new AngularAppEngine(); 284 | 285 | /** 286 | * This is a request handler used by the Angular CLI (dev-server and during build). 287 | */ 288 | const reqHandler = createRequestHandler(async (req: Request) => { 289 | const res: Response|null = await angularApp.render(req); 290 | 291 | // ... 292 | }); 293 | ``` 294 | 295 | ## Configuring a Node.js Server 296 | 297 | The `@angular/ssr/node` extends `@angular/ssr` specifically for Node.js environments. It provides APIs that make it easier to implement server-side rendering within your Node.js application. For a complete list of functions and usage examples, refer to the [`@angular/ssr/node` API reference](api/ssr/node/AngularNodeAppEngine) API reference. 298 | 299 | ```typescript 300 | // server.ts 301 | import { AngularNodeAppEngine, createNodeRequestHandler, writeResponseToNodeResponse } from '@angular/ssr/node'; 302 | import express from 'express'; 303 | 304 | const app = express(); 305 | const angularApp = new AngularNodeAppEngine(); 306 | 307 | app.use('*', (req, res, next) => { 308 | angularApp 309 | .handle(req) 310 | .then(response => { 311 | if (response) { 312 | writeResponseToNodeResponse(response, res); 313 | } else { 314 | next(); // Pass control to the next middleware 315 | } 316 | }) 317 | .catch(next); 318 | }); 319 | 320 | /** 321 | * The request handler used by the Angular CLI (dev-server and during build). 322 | */ 323 | export const reqHandler = createNodeRequestHandler(app); 324 | ``` 325 | -------------------------------------------------------------------------------- /ai-friendly-docs/angular-19.2.3/sections/guide-signals.md: -------------------------------------------------------------------------------- 1 | # Guide Signals 2 | 3 | (From linked-signal.md) 4 | 5 | ## Dependent state with `linkedSignal` 6 | 7 | IMPORTANT: `linkedSignal` is [developer preview](reference/releases#developer-preview). It's ready for you to try, but it might change before it is stable. 8 | 9 | You can use the `signal` function to hold some state in your Angular code. Sometimes, this state depends on some _other_ state. For example, imagine a component that lets the user select a shipping method for an order: 10 | 11 | ```typescript 12 | @Component({/* ... */}) 13 | export class ShippingMethodPicker { 14 | shippingOptions: Signal = getShippingOptions(); 15 | 16 | // Select the first shipping option by default. 17 | selectedOption = signal(this.shippingOptions()[0]); 18 | 19 | changeShipping(newOptionIndex: number) { 20 | this.selectedOption.set(this.shippingOptions()[newOptionIndex]); 21 | } 22 | } 23 | ``` 24 | 25 | In this example, the `selectedOption` defaults to the first option, but changes if the user selects another option. But `shippingOptions` is a signal— its value may change! If `shippingOptions` changes, `selectedOption` may contain a value that is no longer a valid option. 26 | 27 | **The `linkedSignal` function lets you create a signal to hold some state that is intrinsically _linked_ to some other state.** Revisiting the example above, `linkedSignal` can replace `signal`: 28 | 29 | ```typescript 30 | @Component({/* ... */}) 31 | export class ShippingMethodPicker { 32 | shippingOptions: Signal = getShippingOptions(); 33 | 34 | // Initialize selectedOption to the first shipping option. 35 | selectedOption = linkedSignal(() => this.shippingOptions()[0]); 36 | 37 | changeShipping(index: number) { 38 | this.selectedOption.set(this.shippingOptions()[index]); 39 | } 40 | } 41 | ``` 42 | 43 | `linkedSignal` works similarly to `signal` with one key difference— instead of passing a default value, you pass a _computation function_, just like `computed`. When the value of the computation changes, the value of the `linkedSignal` changes to the computation result. This helps ensure that the `linkedSignal` always has a valid value. 44 | 45 | The following example shows how the value of a `linkedSignal` can change based on its linked state: 46 | 47 | ```typescript 48 | const shippingOptions = signal(['Ground', 'Air', 'Sea']); 49 | const selectedOption = linkedSignal(() => shippingOptions()[0]); 50 | console.log(selectedOption()); // 'Ground' 51 | 52 | selectedOption.set(shippingOptions()[2]); 53 | console.log(selectedOption()); // 'Sea' 54 | 55 | shippingOptions.set(['Email', 'Will Call', 'Postal service']); 56 | console.log(selectedOption()); // 'Email' 57 | ``` 58 | 59 | ### Accounting for previous state 60 | 61 | In some cases, the computation for a `linkedSignal` needs to account for the previous value of the `linkedSignal`. 62 | 63 | In the example above, `selectedOption` always updates back to the first option when `shippingOptions` changes. You may, however, want to preserve the user's selection if their selected option is still somewhere in the list. To accomplish this, you can create a `linkedSignal` with a separate _source_ and _computation_: 64 | 65 | ```typescript 66 | @Component({/* ... */}) 67 | export class ShippingMethodPicker { 68 | shippingOptions: Signal = getShippingOptions(); 69 | 70 | selectedOption = linkedSignal({ 71 | // `selectedOption` is set to the `computation` result whenever this `source` changes. 72 | source: this.shippingOptions, 73 | computation: (newOptions, previous) => { 74 | // If the newOptions contain the previously selected option, preserve that selection. 75 | // Otherwise, default to the first option. 76 | return newOptions.find(opt => opt.id === previous?.value?.id) ?? newOptions[0]; 77 | } 78 | }); 79 | 80 | changeShipping(newOptionIndex: number) { 81 | this.selectedOption.set(this.shippingOptions()[newOptionIndex]); 82 | } 83 | } 84 | ``` 85 | 86 | When you create a `linkedSignal`, you can pass an object with separate `source` and `computation` properties instead of providing just a computation. 87 | 88 | The `source` can be any signal, such as a `computed` or component `input`. When the value of `source` changes, `linkedSignal` updates its value to the result of the provided `computation`. 89 | 90 | The `computation` is a function that receives the new value of `source` and a `previous` object. The `previous` object has two properties— `previous.source` is the previous value of `source`, and `previous.value` is the previous result of the `computation`. You can use these previous values to decide the new result of the computation. 91 | 92 | ### Custom equality comparison 93 | 94 | `linkedSignal`, as any other signal, can be configured with a custom equality function. This function is used by downstream dependencies to determine if that value of the `linkedSignal` (result of a computation) changed: 95 | 96 | ```typescript 97 | const activeUser = signal({id: 123, name: 'Morgan', isAdmin: true}); 98 | 99 | const activeUserEditCopy = linkedSignal(() => activeUser(), { 100 | // Consider the user as the same if it's the same `id`. 101 | equal: (a, b) => a.id === b.id, 102 | }); 103 | 104 | // Or, if separating `source` and `computation` 105 | const activeUserEditCopy = linkedSignal({ 106 | source: activeUser, 107 | computation: user => user, 108 | equal: (a, b) => a.id === b.id, 109 | }); 110 | ``` 111 | 112 | --- 113 | 114 | 115 | (From overview.md) 116 | 117 | 118 | Angular Signals is a system that granularly tracks how and where your state is used throughout an application, allowing the framework to optimize rendering updates. 119 | 120 | 121 | Tip: Check out Angular's [Essentials](essentials/signals) before diving into this comprehensive guide. 122 | 123 | ### What are signals? 124 | 125 | A **signal** is a wrapper around a value that notifies interested consumers when that value changes. Signals can contain any value, from primitives to complex data structures. 126 | 127 | You read a signal's value by calling its getter function, which allows Angular to track where the signal is used. 128 | 129 | Signals may be either _writable_ or _read-only_. 130 | 131 | #### Writable signals 132 | 133 | Writable signals provide an API for updating their values directly. You create writable signals by calling the `signal` function with the signal's initial value: 134 | 135 | ```ts 136 | const count = signal(0); 137 | 138 | // Signals are getter functions - calling them reads their value. 139 | console.log('The count is: ' + count()); 140 | ``` 141 | 142 | To change the value of a writable signal, either `.set()` it directly: 143 | 144 | ```ts 145 | count.set(3); 146 | ``` 147 | 148 | or use the `.update()` operation to compute a new value from the previous one: 149 | 150 | ```ts 151 | // Increment the count by 1. 152 | count.update(value => value + 1); 153 | ``` 154 | 155 | Writable signals have the type `WritableSignal`. 156 | 157 | #### Computed signals 158 | 159 | **Computed signal** are read-only signals that derive their value from other signals. You define computed signals using the `computed` function and specifying a derivation: 160 | 161 | ```typescript 162 | const count: WritableSignal = signal(0); 163 | const doubleCount: Signal = computed(() => count() * 2); 164 | ``` 165 | 166 | The `doubleCount` signal depends on the `count` signal. Whenever `count` updates, Angular knows that `doubleCount` needs to update as well. 167 | 168 | ##### Computed signals are both lazily evaluated and memoized 169 | 170 | `doubleCount`'s derivation function does not run to calculate its value until the first time you read `doubleCount`. The calculated value is then cached, and if you read `doubleCount` again, it will return the cached value without recalculating. 171 | 172 | If you then change `count`, Angular knows that `doubleCount`'s cached value is no longer valid, and the next time you read `doubleCount` its new value will be calculated. 173 | 174 | As a result, you can safely perform computationally expensive derivations in computed signals, such as filtering arrays. 175 | 176 | ##### Computed signals are not writable signals 177 | 178 | You cannot directly assign values to a computed signal. That is, 179 | 180 | ```ts 181 | doubleCount.set(3); 182 | ``` 183 | 184 | produces a compilation error, because `doubleCount` is not a `WritableSignal`. 185 | 186 | ##### Computed signal dependencies are dynamic 187 | 188 | Only the signals actually read during the derivation are tracked. For example, in this `computed` the `count` signal is only read if the `showCount` signal is true: 189 | 190 | ```ts 191 | const showCount = signal(false); 192 | const count = signal(0); 193 | const conditionalCount = computed(() => { 194 | if (showCount()) { 195 | return `The count is ${count()}.`; 196 | } else { 197 | return 'Nothing to see here!'; 198 | } 199 | }); 200 | ``` 201 | 202 | When you read `conditionalCount`, if `showCount` is `false` the "Nothing to see here!" message is returned _without_ reading the `count` signal. This means that if you later update `count` it will _not_ result in a recomputation of `conditionalCount`. 203 | 204 | If you set `showCount` to `true` and then read `conditionalCount` again, the derivation will re-execute and take the branch where `showCount` is `true`, returning the message which shows the value of `count`. Changing `count` will then invalidate `conditionalCount`'s cached value. 205 | 206 | Note that dependencies can be removed during a derivation as well as added. If you later set `showCount` back to `false`, then `count` will no longer be considered a dependency of `conditionalCount`. 207 | 208 | ### Reading signals in `OnPush` components 209 | 210 | When you read a signal within an `OnPush` component's template, Angular tracks the signal as a dependency of that component. When the value of that signal changes, Angular automatically [marks](api/core/ChangeDetectorRef#markforcheck) the component to ensure it gets updated the next time change detection runs. Refer to the [Skipping component subtrees](best-practices/skipping-subtrees) guide for more information about `OnPush` components. 211 | 212 | ### Effects 213 | 214 | Signals are useful because they notify interested consumers when they change. An **effect** is an operation that runs whenever one or more signal values change. You can create an effect with the `effect` function: 215 | 216 | ```ts 217 | effect(() => { 218 | console.log(`The current count is: ${count()}`); 219 | }); 220 | ``` 221 | 222 | Effects always run **at least once.** When an effect runs, it tracks any signal value reads. Whenever any of these signal values change, the effect runs again. Similar to computed signals, effects keep track of their dependencies dynamically, and only track signals which were read in the most recent execution. 223 | 224 | Effects always execute **asynchronously**, during the change detection process. 225 | 226 | #### Use cases for effects 227 | 228 | Effects are rarely needed in most application code, but may be useful in specific circumstances. Here are some examples of situations where an `effect` might be a good solution: 229 | 230 | * Logging data being displayed and when it changes, either for analytics or as a debugging tool. 231 | * Keeping data in sync with `window.localStorage`. 232 | * Adding custom DOM behavior that can't be expressed with template syntax. 233 | * Performing custom rendering to a ``, charting library, or other third party UI library. 234 | 235 | 236 | Avoid using effects for propagation of state changes. This can result in `ExpressionChangedAfterItHasBeenChecked` errors, infinite circular updates, or unnecessary change detection cycles. 237 | 238 | Instead, use `computed` signals to model state that depends on other state. 239 | 240 | 241 | #### Injection context 242 | 243 | By default, you can only create an `effect()` within an [injection context](guide/di/dependency-injection-context) (where you have access to the `inject` function). The easiest way to satisfy this requirement is to call `effect` within a component, directive, or service `constructor`: 244 | 245 | ```ts 246 | @Component({...}) 247 | export class EffectiveCounterComponent { 248 | readonly count = signal(0); 249 | constructor() { 250 | // Register a new effect. 251 | effect(() => { 252 | console.log(`The count is: ${this.count()}`); 253 | }); 254 | } 255 | } 256 | ``` 257 | 258 | Alternatively, you can assign the effect to a field (which also gives it a descriptive name). 259 | 260 | ```ts 261 | @Component({...}) 262 | export class EffectiveCounterComponent { 263 | readonly count = signal(0); 264 | 265 | private loggingEffect = effect(() => { 266 | console.log(`The count is: ${this.count()}`); 267 | }); 268 | } 269 | ``` 270 | 271 | To create an effect outside the constructor, you can pass an `Injector` to `effect` via its options: 272 | 273 | ```ts 274 | @Component({...}) 275 | export class EffectiveCounterComponent { 276 | readonly count = signal(0); 277 | constructor(private injector: Injector) {} 278 | 279 | initializeLogging(): void { 280 | effect(() => { 281 | console.log(`The count is: ${this.count()}`); 282 | }, {injector: this.injector}); 283 | } 284 | } 285 | ``` 286 | 287 | #### Destroying effects 288 | 289 | When you create an effect, it is automatically destroyed when its enclosing context is destroyed. This means that effects created within components are destroyed when the component is destroyed. The same goes for effects within directives, services, etc. 290 | 291 | Effects return an `EffectRef` that you can use to destroy them manually, by calling the `.destroy()` method. You can combine this with the `manualCleanup` option to create an effect that lasts until it is manually destroyed. Be careful to actually clean up such effects when they're no longer required. 292 | 293 | ### Advanced topics 294 | 295 | #### Signal equality functions 296 | 297 | When creating a signal, you can optionally provide an equality function, which will be used to check whether the new value is actually different than the previous one. 298 | 299 | ```ts 300 | import _ from 'lodash'; 301 | 302 | const data = signal(['test'], {equal: _.isEqual}); 303 | 304 | // Even though this is a different array instance, the deep equality 305 | // function will consider the values to be equal, and the signal won't 306 | // trigger any updates. 307 | data.set(['test']); 308 | ``` 309 | 310 | Equality functions can be provided to both writable and computed signals. 311 | 312 | HELPFUL: By default, signals use referential equality ([`Object.is()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison). 313 | 314 | #### Reading without tracking dependencies 315 | 316 | Rarely, you may want to execute code which may read signals within a reactive function such as `computed` or `effect` _without_ creating a dependency. 317 | 318 | For example, suppose that when `currentUser` changes, the value of a `counter` should be logged. you could create an `effect` which reads both signals: 319 | 320 | ```ts 321 | effect(() => { 322 | console.log(`User set to ${currentUser()} and the counter is ${counter()}`); 323 | }); 324 | ``` 325 | 326 | This example will log a message when _either_ `currentUser` or `counter` changes. However, if the effect should only run when `currentUser` changes, then the read of `counter` is only incidental and changes to `counter` shouldn't log a new message. 327 | 328 | You can prevent a signal read from being tracked by calling its getter with `untracked`: 329 | 330 | ```ts 331 | effect(() => { 332 | console.log(`User set to ${currentUser()} and the counter is ${untracked(counter)}`); 333 | }); 334 | ``` 335 | 336 | `untracked` is also useful when an effect needs to invoke some external code which shouldn't be treated as a dependency: 337 | 338 | ```ts 339 | effect(() => { 340 | const user = currentUser(); 341 | untracked(() => { 342 | // If the `loggingService` reads signals, they won't be counted as 343 | // dependencies of this effect. 344 | this.loggingService.log(`User set to ${user}`); 345 | }); 346 | }); 347 | ``` 348 | 349 | #### Effect cleanup functions 350 | 351 | Effects might start long-running operations, which you should cancel if the effect is destroyed or runs again before the first operation finished. When you create an effect, your function can optionally accept an `onCleanup` function as its first parameter. This `onCleanup` function lets you register a callback that is invoked before the next run of the effect begins, or when the effect is destroyed. 352 | 353 | ```ts 354 | effect((onCleanup) => { 355 | const user = currentUser(); 356 | 357 | const timer = setTimeout(() => { 358 | console.log(`1 second ago, the user became ${user}`); 359 | }, 1000); 360 | 361 | onCleanup(() => { 362 | clearTimeout(timer); 363 | }); 364 | }); 365 | ``` 366 | 367 | ### Using signals with RxJS 368 | 369 | See [RxJS interop with Angular signals](ecosystem/rxjs-interop) for details on interoperability between signals and RxJS. 370 | 371 | --- 372 | 373 | 374 | (From resource.md) 375 | 376 | ## Async reactivity with resources 377 | 378 | IMPORTANT: `resource` is [experimental](reference/releases#experimental). It's ready for you to try, but it might change before it is stable. 379 | 380 | Most signal APIs are synchronous— `signal`, `computed`, `input`, etc. However, applications often need to deal with data that is available asynchronously. A `Resource` gives you a way to incorporate async data into your application's signal-based code. 381 | 382 | You can use a `Resource` to perform any kind of async operation, but the most common use-case for `Resource` is fetching data from a server. The following example creates a resource to fetch some user data. 383 | 384 | The easiest way to create a `Resource` is the `resource` function. 385 | 386 | ```typescript 387 | import {resource, Signal} from '@angular/core'; 388 | 389 | const userId: Signal = getUserId(); 390 | 391 | const userResource = resource({ 392 | // Define a reactive request computation. 393 | // The request value recomputes whenever any read signals change. 394 | request: () => ({id: userId()}), 395 | 396 | // Define an async loader that retrieves data. 397 | // The resource calls this function every time the `request` value changes. 398 | loader: ({request}) => fetchUser(request), 399 | }); 400 | 401 | // Create a computed signal based on the result of the resource's loader function. 402 | const firstName = computed(() => userResource.value().firstName); 403 | ``` 404 | 405 | The `resource` function accepts a `ResourceOptions` object with two main properties: `request` and `loader`. 406 | 407 | The `request` property defines a reactive computation that produce a request value. Whenever signals read in this computation change, the resource produces a new request value, similar to `computed`. 408 | 409 | The `loader` property defines a `ResourceLoader`— an async function that retrieves some state. The resource calls the loader every time the `request` computation produces a new value, passing that value to the loader. See [Resource loaders](#resource-loaders) below for more details. 410 | 411 | `Resource` has a `value` signal that contains the results of the loader. 412 | 413 | ### Resource loaders 414 | 415 | When creating a resource, you specify a `ResourceLoader`. This loader is an async function that accepts a single parameter— a `ResourceLoaderParams` object— and returns a value. 416 | 417 | The `ResourceLoaderParams` object contains three properties: `request`, `previous`, and `abortSignal`. 418 | 419 | | Property | Description | 420 | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | 421 | | `request` | The value of the resource's `request` computation. | 422 | | `previous` | An object with a `status` property, containing the previous `ResourceStatus`. | 423 | | `abortSignal` | An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). See [Aborting requests](#aborting-requests) below for details. | 424 | 425 | 426 | If the `request` computation returns `undefined`, the loader function does not run and the resource status becomes `Idle`. 427 | 428 | #### Aborting requests 429 | 430 | A resource aborts an outstanding request if the `request` computation changes while the resource is loading. 431 | 432 | You can use the `abortSignal` in `ResourceLoaderParams` to respond to aborted requests. For example, the native `fetch` function accepts an `AbortSignal`: 433 | 434 | ```typescript 435 | const userId: Signal = getUserId(); 436 | 437 | const userResource = resource({ 438 | request: () => ({id: userId()}), 439 | loader: ({request, abortSignal}): Promise => { 440 | // fetch cancels any outstanding HTTP requests when the given `AbortSignal` 441 | // indicates that the request has been aborted. 442 | return fetch(`users/${request.id}`, {signal: abortSignal}); 443 | }, 444 | }); 445 | ``` 446 | 447 | See [`AbortSignal` on MDN](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) for more details on request cancellation with `AbortSignal`. 448 | 449 | #### Reloading 450 | 451 | You can programmatically trigger a resource's `loader` by calling the `reload` method. 452 | 453 | ```typescript 454 | const userId: Signal = getUserId(); 455 | 456 | const userResource = resource({ 457 | request: () => ({id: userId()}), 458 | loader: ({request}) => fetchUser(request), 459 | }); 460 | 461 | // ... 462 | 463 | userResource.reload(); 464 | ``` 465 | 466 | ### Resource status 467 | 468 | The resource object has several signal properties for reading the status of the asynchronous loader. 469 | 470 | | Property | Description | 471 | | ----------- | --------------------------------------------------------------------------------------------------------------- | 472 | | `value` | The most recent value of the resource, or `undefined` if no value has been received. | 473 | | `hasValue` | Whether the resource has a value. | 474 | | `error` | The most recent error encountered while running the resource's loader, or `undefined` if no error has occurred. | 475 | | `isLoading` | Whether the resource loader is currently running. | 476 | | `status` | The resource's specific `ResourceStatus`, as described below. | 477 | 478 | The `status` signal provides a specific `ResourceStatus` that describes the state of the resource. 479 | 480 | | Status | `value()` | Description | 481 | | ----------- | :---------------- | ---------------------------------------------------------------------------- | 482 | | `Idle` | `undefined` | The resource has no valid request and the loader has not run. | 483 | | `Error` | `undefined` | The loader has encountered an error. | 484 | | `Loading` | `undefined` | The loader is running as a result of the `request` value changing. | 485 | | `Reloading` | Previous value | The loader is running as a result calling of the resource's `reload` method. | 486 | | `Resolved` | Resolved value | The loader has completed. | 487 | | `Local` | Locally set value | The resource's value has been set locally via `.set()` or `.update()` | 488 | 489 | You can use this status information to conditionally display user interface elements, such loading indicators and error messages. 490 | 491 | --- --------------------------------------------------------------------------------