diff --git a/adev/src/content/best-practices/error-handling.md b/adev/src/content/best-practices/error-handling.md
index 36dbb051776..e2086b8390c 100644
--- a/adev/src/content/best-practices/error-handling.md
+++ b/adev/src/content/best-practices/error-handling.md
@@ -16,13 +16,33 @@ Angular does _not_ catch errors inside of APIs that are called directly by your
Angular catches _asynchronous_ errors from user promises or observables only when:
-* There is an explicit contract for Angular to wait for and use the result of the asynchronous operation, and
-* When errors are not presented in the return value or state.
+- There is an explicit contract for Angular to wait for and use the result of the asynchronous operation, and
+- When errors are not presented in the return value or state.
For example, `AsyncPipe` and `PendingTasks.run` forward errors to the `ErrorHandler`, whereas `resource` presents the error in the `status` and `error` properties.
Errors that Angular reports to the `ErrorHandler` are _unexpected_ errors. These errors may be unrecoverable or an indication that the state of the application is corrupted. Applications should provide error handling using `try` blocks or appropriate error handling operators (like `catchError` in RxJS) where the error occurs whenever possible rather than relying on the `ErrorHandler`, which is most frequently and appropriately used only as a mechanism to report potentially fatal errors to the error tracking and logging infrastructure.
+```ts
+export class GlobalErrorHandler implements ErrorHandler {
+ private readonly analyticsService = inject(AnalyticsService);
+ private readonly router = inject(Router);
+
+ handleError(error: any) {
+ const url = this.router.url;
+ const errorMessage = error?.message ?? 'unknown';
+
+ this.analyticsService.trackEvent({
+ eventName: 'exception',
+ description: `Screen: ${url} | ${errorMessage}`,
+ });
+
+ console.error(GlobalErrorHandler.name, { error });
+ }
+}
+
+```
+
### `TestBed` rethrows errors by default
In many cases, `ErrorHandler` may only log errors and otherwise allow the application to continue running. In tests, however, you almost always want to surface these errors. Angular's `TestBed` rethrows unexpected errors to ensure that errors caught by the framework cannot be unintentionally missed or ignored. In rare circumstances, a test may specifically attempt to ensure errors do not cause the application to be unresponsive or crash. In these situations, you can [configure `TestBed` to _not_ rethrow application errors](api/core/testing/TestModuleMetadata#rethrowApplicationErrors) with `TestBed.configureTestingModule({rethrowApplicationErrors: false})`.
diff --git a/adev/src/content/ecosystem/rxjs-interop/signals-interop.md b/adev/src/content/ecosystem/rxjs-interop/signals-interop.md
index e1bf6da5cb5..78b3758256e 100644
--- a/adev/src/content/ecosystem/rxjs-interop/signals-interop.md
+++ b/adev/src/content/ecosystem/rxjs-interop/signals-interop.md
@@ -146,7 +146,7 @@ export class UserProfile {
protected userId = input();
private userResource = rxResource({
- params: () => this.userId(),
+ params: () => ({ userId: this.userId() }),
// The `stream` property expects a factory function that returns
// a data stream as an RxJS Observable.
diff --git a/adev/src/content/examples/drag-drop/src/axis-lock/app/app.component.ts b/adev/src/content/examples/drag-drop/src/axis-lock/app/app.component.ts
index 579ecbf34f0..0ebe9ff5ce3 100755
--- a/adev/src/content/examples/drag-drop/src/axis-lock/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/axis-lock/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-axis-lock-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDrag],
})
export class CdkDragDropAxisLockExample {}
diff --git a/adev/src/content/examples/drag-drop/src/connected-sorting-group/app/app.component.ts b/adev/src/content/examples/drag-drop/src/connected-sorting-group/app/app.component.ts
index 08cc08275cc..4da8ffddf37 100755
--- a/adev/src/content/examples/drag-drop/src/connected-sorting-group/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/connected-sorting-group/app/app.component.ts
@@ -15,7 +15,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-connected-sorting-group-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropListGroup, CdkDropList, CdkDrag],
})
export class CdkDragDropConnectedSortingGroupExample {
diff --git a/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.ts b/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.ts
index 68de4c7bfe4..4fbb6d51828 100755
--- a/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/connected-sorting/app/app.component.ts
@@ -14,7 +14,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-connected-sorting-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropList, CdkDrag],
})
export class CdkDragDropConnectedSortingExample {
diff --git a/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.ts b/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.ts
index 2643d24f0a8..54f433aa3ab 100755
--- a/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/custom-handle/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-handle-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDrag, CdkDragHandle],
})
export class CdkDragDropHandleExample {}
diff --git a/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.ts b/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.ts
index 7be12021a6f..799069e23f5 100755
--- a/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/custom-placeholder/app/app.component.ts
@@ -14,7 +14,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-custom-placeholder-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropList, CdkDrag, CdkDragPlaceholder],
})
export class CdkDragDropCustomPlaceholderExample {
diff --git a/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.ts b/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.ts
index 015e65f20ae..adc079e5f16 100755
--- a/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/custom-preview/app/app.component.ts
@@ -14,7 +14,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-custom-preview-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropList, CdkDrag, CdkDragPreview],
})
export class CdkDragDropCustomPreviewExample {
diff --git a/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.ts b/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.ts
index 77396bc9c03..e634d53bfe2 100755
--- a/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/delay-drag/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-delay-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDrag],
})
export class CdkDragDropDelayExample {}
diff --git a/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.ts b/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.ts
index 6ea1a6e2a41..4d25d457466 100755
--- a/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/disable-drag/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-disabled-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropList, CdkDrag],
})
export class CdkDragDropDisabledExample {
diff --git a/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.ts b/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.ts
index b4e88d1bfee..5c91b047a40 100755
--- a/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/disable-sorting/app/app.component.ts
@@ -15,7 +15,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-disabled-sorting-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropListGroup, CdkDropList, CdkDrag],
})
export class CdkDragDropDisabledSortingExample {
diff --git a/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.ts b/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.ts
index f00b36863c7..bf66690a44b 100755
--- a/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/enter-predicate/app/app.component.ts
@@ -14,7 +14,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-enter-predicate-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropList, CdkDrag],
})
export class CdkDragDropEnterPredicateExample {
diff --git a/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.ts b/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.ts
index e022b667bd7..ddc93e7e045 100755
--- a/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/free-drag-position/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-free-drag-position-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDrag],
})
export class CdkDragDropFreeDragPositionExample {
diff --git a/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.ts b/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.ts
index 1a0e81ca738..2192aa4a8ac 100755
--- a/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/horizontal-sorting/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-horizontal-sorting-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropList, CdkDrag],
})
export class CdkDragDropHorizontalSortingExample {
diff --git a/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.ts b/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.ts
index 8d177182791..65f7d0ad443 100755
--- a/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/mixed-sorting/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-mixed-sorting-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropList, CdkDrag],
})
export class CdkDragDropMixedSortingExample {
diff --git a/adev/src/content/examples/drag-drop/src/overview/app/app.component.ts b/adev/src/content/examples/drag-drop/src/overview/app/app.component.ts
index ebee20d9ae4..398e0c61bf0 100755
--- a/adev/src/content/examples/drag-drop/src/overview/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/overview/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-overview-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDrag],
})
export class CdkDragDropOverviewExample {}
diff --git a/adev/src/content/examples/drag-drop/src/root-element/app/app.component.ts b/adev/src/content/examples/drag-drop/src/root-element/app/app.component.ts
index 58c3003b11d..f14961c06f5 100755
--- a/adev/src/content/examples/drag-drop/src/root-element/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/root-element/app/app.component.ts
@@ -18,7 +18,6 @@ import {
selector: 'cdk-drag-drop-root-element-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDrag],
})
export class CdkDragDropRootElementExample implements AfterViewInit, OnDestroy {
diff --git a/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.ts b/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.ts
index 3077866d4e7..909c1102c9a 100755
--- a/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/sort-predicate/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-sort-predicate-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropList, CdkDrag],
})
export class CdkDragDropSortPredicateExample {
diff --git a/adev/src/content/examples/drag-drop/src/sorting/app/app.component.ts b/adev/src/content/examples/drag-drop/src/sorting/app/app.component.ts
index 040abc89095..b1b77d85ffd 100755
--- a/adev/src/content/examples/drag-drop/src/sorting/app/app.component.ts
+++ b/adev/src/content/examples/drag-drop/src/sorting/app/app.component.ts
@@ -8,7 +8,6 @@ import {Component} from '@angular/core';
selector: 'cdk-drag-drop-sorting-example',
templateUrl: 'app.component.html',
styleUrl: 'app.component.css',
- standalone: true,
imports: [CdkDropList, CdkDrag],
})
export class CdkDragDropSortingExample {
diff --git a/adev/src/content/examples/elements/src/app/popup.service.ts b/adev/src/content/examples/elements/src/app/popup.service.ts
index dbc58846bea..ca2a4029a8a 100644
--- a/adev/src/content/examples/elements/src/app/popup.service.ts
+++ b/adev/src/content/examples/elements/src/app/popup.service.ts
@@ -1,13 +1,17 @@
-import {ApplicationRef, createComponent, EnvironmentInjector, Injectable} from '@angular/core';
+import {
+ ApplicationRef,
+ createComponent,
+ EnvironmentInjector,
+ inject,
+ Injectable,
+} from '@angular/core';
import {NgElement, WithProperties} from '@angular/elements';
import {PopupComponent} from './popup.component';
@Injectable()
export class PopupService {
- constructor(
- private injector: EnvironmentInjector,
- private applicationRef: ApplicationRef,
- ) {}
+ private readonly injector = inject(EnvironmentInjector);
+ private readonly applicationRef = inject(ApplicationRef);
// Previous dynamic-loading method required you to set up infrastructure
// before adding the popup to the DOM.
diff --git a/adev/src/content/guide/components/programmatic-rendering.md b/adev/src/content/guide/components/programmatic-rendering.md
index 678d49dc219..51887ae3591 100644
--- a/adev/src/content/guide/components/programmatic-rendering.md
+++ b/adev/src/content/guide/components/programmatic-rendering.md
@@ -2,16 +2,16 @@
TIP: This guide assumes you've already read the [Essentials Guide](essentials). Read that first if you're new to Angular.
-In addition to using a component directly in a template, you can also dynamically render components
-programmatically. This is helpful for situations when a component is unknown initially (thus can not
+In addition to using a component directly in a template, you can also dynamically render components
+programmatically. This is helpful for situations when a component is unknown initially (thus can not
be referenced in a template directly) and it depends on some conditions.
There are two main ways to render a component programmatically: in a template using `NgComponentOutlet`,
-or in your TypeScript code using `ViewContainerRef`.
+or in your TypeScript code using `ViewContainerRef`.
-HELPFUL: for lazy-loading use-cases (for example if you want to delay loading of a heavy component), consider
-using the built-in [`@defer` feature](/guide/templates/defer) instead. The `@defer` feature allows the code
-of any components, directives, and pipes inside the `@defer` block to be extracted into separate JavaScript
+HELPFUL: for lazy-loading use-cases (for example if you want to delay loading of a heavy component), consider
+using the built-in [`@defer` feature](/guide/templates/defer) instead. The `@defer` feature allows the code
+of any components, directives, and pipes inside the `@defer` block to be extracted into separate JavaScript
chunks automatically and loaded only when necessary, based on the configured triggers.
## Using NgComponentOutlet
@@ -103,10 +103,10 @@ In the example above, clicking the "Load content" button results in the followin
## Lazy-loading components
-HELPFUL: if you want to lazy-load some components, you may consider using the built-in [`@defer` feature](/guide/templates/defer)
+HELPFUL: if you want to lazy-load some components, you may consider using the built-in [`@defer` feature](/guide/templates/defer)
instead.
-If your use-case is not covered by the `@defer` feature, you can use either `NgComponentOutlet` or
+If your use-case is not covered by the `@defer` feature, you can use either `NgComponentOutlet` or
`ViewContainerRef` with a standard JavaScript [dynamic import](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/import).
```angular-ts
@@ -119,9 +119,11 @@ If your use-case is not covered by the `@defer` feature, you can use either `NgC
Advanced settings
-
+ @if(!advancedSettings) {
+
+ }
`
@@ -137,3 +139,117 @@ export class AdminSettings {
```
The example above loads and displays the `AdvancedSettings` upon receiving a button click.
+
+## Binding inputs, outputs and setting host directives at creation
+
+When dynamically creating components, manually setting inputs and subscribing to outputs can be error-prone. You often need to write extra code just to wire up bindings after the component is instantiated.
+
+To simplify this, both `createComponent` and `ViewContainerRef.createComponent` support passing a `bindings` array with helpers like `inputBinding()`, `outputBinding()`, and `twoWayBinding()` to configure inputs and outputs up front. You can also specify a `directives` array to apply any host directives. This enables creating components programmatically with template-like bindings in a single, declarative call.
+
+### Host view using `ViewContainerRef.createComponent`
+
+`ViewContainerRef.createComponent` creates a component and automatically inserts its host view and host element into the container’s view hierarchy at the container’s location. Use this when the dynamic component should become part of the container’s logical and visual structure (for example, adding list items or inline UI).
+
+By contrast, the standalone `createComponent` API does not attach the new component to any existing view or DOM location — it returns a `ComponentRef` and gives you explicit control over where to place the component’s host element.
+
+```angular-ts
+import { Component, input, model, output } from "@angular/core";
+
+@Component({
+ selector: 'app-warning',
+ template: `
+ @if(isExpanded()) {
+
+
Warning: Action needed!
+
+
+ }
+ `
+})
+export class AppWarningComponent {
+ readonly canClose = input.required();
+ readonly isExpanded = model();
+ readonly close = output();
+}
+```
+
+```ts
+import { Component, ViewContainerRef, signal, inputBinding, outputBinding, twoWayBinding, inject } from '@angular/core';
+import { FocusTrap } from "@angular/cdk/a11y";
+import { ThemeDirective } from '../theme.directive';
+
+@Component({
+ template: ``
+})
+export class HostComponent {
+ private vcr = inject(ViewContainerRef);
+ readonly canClose = signal(true);
+ readonly isExpanded = signal(true);
+
+ showWarning() {
+ const compRef = this.vcr.createComponent(AppWarningComponent, {
+ bindings: [
+ inputBinding('canClose', this.canClose),
+ twoWayBinding('isExpanded', this.isExpanded),
+ outputBinding('close', (confirmed) => {
+ console.log('Closed with result:', confirmed);
+ })
+ ],
+ directives: [
+ FocusTrap,
+ { type: ThemeDirective, bindings: [inputBinding('theme', () => 'warning')] }
+ ]
+ });
+ }
+}
+```
+
+In the example above, the dynamic **AppWarningComponent** is created with its `canClose` input bound to a reactive signal, a two-way binding on its `isExpanded` state, and an output listener for `close`. The `FocusTrap` and `ThemeDirective` are attached to the host element via `directives`.
+
+### Popup attached to `document.body` with `createComponent` + `hostElement`
+
+Use this when rendering outside the current view hierarchy (e.g., overlays). The provided `hostElement` becomes the component’s host in the DOM, so Angular doesn’t create a new element matching the selector. Lets you configure **bindings** directly.
+
+```ts
+import {
+ ApplicationRef,
+ createComponent,
+ EnvironmentInjector,
+ inject,
+ Injectable,
+ inputBinding,
+ outputBinding,
+} from '@angular/core';
+import { PopupComponent } from './popup.component';
+
+@Injectable({ providedIn: 'root' })
+export class PopupService {
+ private readonly injector = inject(EnvironmentInjector);
+ private readonly appRef = inject(ApplicationRef);
+
+ show(message: string) {
+ // Create a host element for the popup
+ const host = document.createElement('popup-host');
+
+ // Create the component and bind in one call
+ const ref = createComponent(PopupComponent, {
+ environmentInjector: this.injector,
+ hostElement: host,
+ bindings: [
+ inputBinding('message', () => message),
+ outputBinding('closed', () => {
+ document.body.removeChild(host);
+ this.appRef.detachView(ref.hostView);
+ ref.destroy();
+ }),
+ ],
+ });
+
+ // Registers the component’s view so it participates in change detection cycle.
+ this.appRef.attachView(ref.hostView);
+ // Inserts the provided host element into the DOM (outside the normal Angular view hierarchy).
+ // This is what makes the popup visible on screen, typically used for overlays or modals.
+ document.body.appendChild(host);
+ }
+}
+```
diff --git a/adev/src/content/guide/di/dependency-injection-context.md b/adev/src/content/guide/di/dependency-injection-context.md
index b242b87d2c8..c011766e5bb 100644
--- a/adev/src/content/guide/di/dependency-injection-context.md
+++ b/adev/src/content/guide/di/dependency-injection-context.md
@@ -5,11 +5,11 @@ This means that injectors can only work when code is executed in such a context.
The injection context is available in these situations:
-* During construction (via the `constructor`) of a class being instantiated by the DI system, such as an `@Injectable` or `@Component`.
-* In the initializer for fields of such classes.
-* In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`.
-* In the `factory` function specified for an `InjectionToken`.
-* Within a stack frame that runs in an injection context.
+- During construction (via the `constructor`) of a class being instantiated by the DI system, such as an `@Injectable` or `@Component`.
+- In the initializer for fields of such classes.
+- In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`.
+- In the `factory` function specified for an `InjectionToken`.
+- Within a stack frame that runs in an injection context.
Knowing when you are in an injection context will allow you to use the [`inject`](api/core/inject) function to inject instances.
@@ -66,7 +66,32 @@ Note that `inject` will return an instance only if the injector can resolve the
## Asserts the context
-Angular provides the `assertInInjectionContext` helper function to assert that the current context is an injection context.
+Angular provides the `assertInInjectionContext` helper function to assert that the current context is an injection context and throws a clear error if not. Pass a reference to the calling function so the error message points to the correct API entry point. This produces a clearer, more actionable message than the default generic injection error.
+
+```ts
+import { ElementRef, assertInInjectionContext, inject } from '@angular/core';
+
+export function injectNativeElement(): T {
+ assertInInjectionContext(injectNativeElement);
+ return inject(ElementRef).nativeElement;
+}
+```
+
+You can then call this helper **from an injection context** (constructor, field initializer, provider factory, or code executed via `runInInjectionContext`):
+
+```ts
+import { Component, inject } from '@angular/core';
+import { injectNativeElement } from './dom-helpers';
+
+@Component({ /* … */ })
+export class PreviewCard {
+ readonly hostEl = injectNativeElement(); // Field initializer runs in an injection context.
+
+ onAction() {
+ const anotherRef = injectNativeElement(); // Fails: runs outside an injection context.
+ }
+}
+```
## Using DI outside of a context
diff --git a/adev/src/content/guide/drag-drop.md b/adev/src/content/guide/drag-drop.md
index 50f89e33652..64d4fc4afec 100644
--- a/adev/src/content/guide/drag-drop.md
+++ b/adev/src/content/guide/drag-drop.md
@@ -34,7 +34,6 @@ import {CdkDrag} from '@angular/cdk/drag-drop';
@Component({
selector: 'my-custom-component',
templateUrl: 'my-custom-component.html',
- standalone: true,
imports: [CdkDrag],
})
export class DragDropExample {}
diff --git a/adev/src/content/guide/elements.md b/adev/src/content/guide/elements.md
index 9d2a298ff80..9cd53266574 100644
--- a/adev/src/content/guide/elements.md
+++ b/adev/src/content/guide/elements.md
@@ -18,22 +18,22 @@ Once a custom element is added to the DOM for any page, it looks and behaves lik
To add the `@angular/elements` package to your workspace, run the following command:
-
+```shell
npm install @angular/elements --save
-
+```
### How it works
The `createCustomElement()` function converts a component into a class that can be registered with the browser as a custom element.
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:
-
+```html
-
+```
When your custom element is placed on a page, the browser creates an instance of the registered class and adds it to the DOM.
The content is provided by the component's template, which uses Angular template syntax, and is rendered using the component and DOM data.
@@ -58,14 +58,14 @@ One regular Angular component and a second one using the custom element.
A custom element _hosts_ an Angular component, providing a bridge between the data and logic defined in the component and standard DOM APIs.
Component properties and logic maps directly into HTML attributes and the browser's event system.
-* The creation API parses the component looking for input properties, and defines corresponding attributes for the custom element.
+- The creation API parses the component looking for input properties, and defines corresponding attributes for the custom element.
It transforms the property names to make them compatible with custom elements, which do not recognize case distinctions.
The resulting attribute names use dash-separated lowercase.
For example, for a component with `inputProp = input({alias: 'myInputProp'})`, the corresponding custom element defines an attribute `my-input-prop`.
-* 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.
- For example, for a component `with valueChanged = output()`, the corresponding custom element dispatches events with the name "valueChanged", and the emitted data is stored on the event's `detail` property.
- If you provide an alias, that value is used; for example, `clicks = output({alias: 'myClick'});` results in dispatch events with the name "myClick".
+- 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.
+ For example, for a component `with valueChanged = output()`, the corresponding custom element dispatches events with the name "valueChanged", and the emitted data is stored on the event's `detail` property.
+ If you provide an alias, that value is used; for example, `clicks = output({alias: 'myClick'});` results in dispatch events with the name "myClick".
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).
@@ -109,33 +109,33 @@ For example, our `popup-element` has a `message` property of type `string`.
There are a few options if you want to get correct types for your custom elements.
Assume you create a `my-dialog` custom element based on the following component:
-
+```ts
@Component(…)
class MyDialog {
content = input(string);
}
-
+```
The most straightforward way to get accurate typings is to cast the return value of the relevant DOM methods to the correct type.
For that, use the `NgElement` and `WithProperties` types \(both exported from `@angular/elements`\):
-
+```ts
const aDialog = document.createElement('my-dialog') as NgElement & WithProperties<{content: string}>;
aDialog.content = 'Hello, world!';
-aDialog.content = 123; // <-- ERROR: TypeScript knows this should be a string.
-aDialog.body = 'News'; // <-- ERROR: TypeScript knows there is no `body` property on `aDialog`.
+aDialog.content = 123; // <-- ERROR: TypeScript knows this should be a string.
+aDialog.body = 'News'; // <-- ERROR: TypeScript knows there is no `body` property on `aDialog`.
-
+```
This is a good way to quickly get TypeScript features, such as type checking and autocomplete support, for your custom element.
But it can get cumbersome if you need it in several places, because you have to cast the return type on every occurrence.
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.\):
-
+```ts
declare global {
interface HTMLElementTagNameMap {
@@ -145,18 +145,18 @@ declare global {
}
}
-
+```
Now, TypeScript can infer the correct type the same way it does for built-in elements:
-
+```ts
document.createElement('div') //--> HTMLDivElement (built-in element)
document.querySelector('foo') //--> Element (unknown element)
document.createElement('my-dialog') //--> NgElement & WithProperties<{content: string}> (custom element)
document.querySelector('my-other-element') //--> NgElement & WithProperties<{foo: 'bar'}> (custom element)
-
+```
## Limitations
diff --git a/adev/src/content/guide/forms/reactive-forms.md b/adev/src/content/guide/forms/reactive-forms.md
index d1fd267e928..8c091c0174c 100644
--- a/adev/src/content/guide/forms/reactive-forms.md
+++ b/adev/src/content/guide/forms/reactive-forms.md
@@ -388,6 +388,135 @@ Initially, the form contains one `Alias` field. To add another field, click the
+## Unified control state change events
+
+All form controls expose a single unified stream of **control state change events** through the `events` observable on `AbstractControl` (`FormControl`, `FormGroup`, `FormArray`, and `FormRecord`).
+This unified stream lets you react to **value**, **status**, **pristine**, **touched** and **reset** state changes and also for **form-level actions** such as **submit** , allowing you to handle all updates with a one subscription instead of wiring multiple observables.
+
+### Event types
+
+Each item emitted by `events` is an instance of a specific event class:
+
+- **`ValueChangeEvent`** — when the control’s **value** changes.
+- **`StatusChangeEvent`** — when the control’s **validation status** updates to one of the `FormControlStatus` values (`VALID`, `INVALID`, `PENDING`, or `DISABLED`).
+- **`PristineChangeEvent`** — when the control’s **pristine/dirty** state changes.
+- **`TouchedChangeEvent`** — when the control’s **touched/untouched** state changes.
+- **`FormResetEvent`** — when a control or form is reset, either via the `reset()` API or a native action.
+- **`FormSubmittedEvent`** — when the form is submitted.
+
+All event classes extend `ControlEvent` and include a `source` reference to the `AbstractControl` that originated the change, which is useful in large forms.
+
+```ts
+import { Component } from '@angular/core';
+import {
+ FormControl,
+ ValueChangeEvent,
+ StatusChangeEvent,
+ PristineChangeEvent,
+ TouchedChangeEvent,
+ FormResetEvent,
+ FormSubmittedEvent,
+ ReactiveFormsModule,
+ FormGroup,
+} from '@angular/forms';
+
+@Component({/* ... */ })
+export class UnifiedEventsBasicComponent {
+ form = new FormGroup({
+ username: new FormControl(''),
+ });
+
+ constructor() {
+ this.form.events.subscribe((e) => {
+ if (e instanceof ValueChangeEvent) {
+ console.log('Value changed to: ', e.value);
+ }
+
+ if (e instanceof StatusChangeEvent) {
+ console.log('Status changed to: ', e.status);
+ }
+
+ if (e instanceof PristineChangeEvent) {
+ console.log('Pristine status changed to: ', e.pristine);
+ }
+
+ if (e instanceof TouchedChangeEvent) {
+ console.log('Touched status changed to: ', e.touched);
+ }
+
+ if (e instanceof FormResetEvent) {
+ console.log('Form was reset');
+ }
+
+ if (e instanceof FormSubmittedEvent) {
+ console.log('Form was submitted');
+ }
+ });
+ }
+}
+```
+
+### Filtering specific events
+
+Prefer RxJS operators when you only need a subset of event types.
+
+```ts
+import { filter } from 'rxjs/operators';
+import { StatusChangeEvent } from '@angular/forms';
+
+control.events
+ .pipe(filter((e) => e instanceof StatusChangeEvent))
+ .subscribe((e) => console.log('Status:', e.status));
+```
+
+### Unifying from multiple subscriptions
+
+**Before**
+
+```ts
+import { combineLatest } from 'rxjs/operators';
+
+combineLatest([control.valueChanges, control.statusChanges])
+ .subscribe(([value, status]) => { /* ... */ });
+```
+
+**After**
+
+```ts
+control.events.subscribe((e) => {
+ // Handle ValueChangeEvent, StatusChangeEvent, etc.
+});
+```
+
+NOTE: On value change, the emit happens right after a value of this control is updated. The value of a parent control (for example if this FormControl is a part of a FormGroup) is updated later, so accessing a value of a parent control (using the `value` property) from the callback of this event might result in getting a value that has not been updated yet. Subscribe to the `events` of the parent control instead.
+
+## Utility functions for narrowing form control types
+
+Angular provides four utility functions that help determine the concrete type of an `AbstractControl`. These functions act as **type guards** and narrow the control type when they return `true`, which lets you safely access subtype-specific properties inside the same block.
+
+| Utility function | Details |
+| :--------------- | :-------------------------------------------------- |
+| `isFormControl` | Returns `true` when the control is a `FormControl`. |
+| `isFormGroup` | Returns `true` when the control is a `FormGroup` |
+| `isFormRecord` | Returns `true` when the control is a `FormRecord` |
+| `isFormArray` | Returns `true` when the control is a `FormArray` |
+
+These helpers are particularly useful in **custom validators**, where the function signature receives an `AbstractControl`, but the logic is intended for a specific control kind.
+
+```ts
+import { AbstractControl, isFormArray } from '@angular/forms';
+
+export function positiveValues(control: AbstractControl) {
+ if (!isFormArray(control)) {
+ return null; // Not a FormArray: validator is not applicable.
+ }
+
+ // Safe to access FormArray-specific API after narrowing.
+ const hasNegative = control.controls.some(c => c.value < 0);
+ return hasNegative ? { positiveValues: true } : null;
+}
+```
+
## Reactive forms API summary
The following table lists the base classes and services used to create and manage reactive form controls.
diff --git a/adev/src/content/guide/image-optimization.md b/adev/src/content/guide/image-optimization.md
index 44abc3ae5fa..74696e59b63 100644
--- a/adev/src/content/guide/image-optimization.md
+++ b/adev/src/content/guide/image-optimization.md
@@ -4,18 +4,18 @@ The `NgOptimizedImage` directive makes it easy to adopt performance best practic
The directive ensures that the loading of the [Largest Contentful Paint (LCP)](http://web.dev/lcp) image is prioritized by:
-* Automatically setting the `fetchpriority` attribute on the `` tag
-* Lazy loading other images by default
-* Automatically generating a preconnect link tag in the document head
-* Automatically generating a `srcset` attribute
-* Generating a [preload hint](https://developer.mozilla.org/docs/Web/HTML/Link_types/preload) if app is using SSR
+- Automatically setting the `fetchpriority` attribute on the `` tag
+- Lazy loading other images by default
+- Automatically generating a preconnect link tag in the document head
+- Automatically generating a `srcset` attribute
+- Generating a [preload hint](https://developer.mozilla.org/docs/Web/HTML/Link_types/preload) if app is using SSR
In addition to optimizing the loading of the LCP image, `NgOptimizedImage` enforces a number of image best practices, such as:
-* Using [image CDN URLs to apply image optimizations](https://web.dev/image-cdns/#how-image-cdns-use-urls-to-indicate-optimization-options)
-* Preventing layout shift by requiring `width` and `height`
-* Warning if `width` or `height` have been set incorrectly
-* Warning if the image will be visually distorted when rendered
+- Using [image CDN URLs to apply image optimizations](https://web.dev/image-cdns/#how-image-cdns-use-urls-to-indicate-optimization-options)
+- Preventing layout shift by requiring `width` and `height`
+- Warning if `width` or `height` have been set incorrectly
+- Warning if the image will be visually distorted when rendered
If you're using a background image in CSS, [start here](#how-to-migrate-your-background-image).
@@ -27,20 +27,20 @@ If you're using a background image in CSS, [start here](#how-to-migrate-your-bac
Import `NgOptimizedImage` directive from `@angular/common`:
-
+```ts
import { NgOptimizedImage } from '@angular/common'
-
+```
and include it into the `imports` array of a standalone component or an NgModule:
-
-
+```ts
imports: [
NgOptimizedImage,
// ...
],
+```
@@ -52,39 +52,39 @@ A brief guide for setting up a loader can be found in the [Configuring an Image
To activate the `NgOptimizedImage` directive, replace your image's `src` attribute with `ngSrc`.
-
+```html
-
+```
If you're using a [built-in third-party loader](#built-in-loaders), make sure to omit the base URL path from `src`, as that will be prepended automatically by the loader.
Always mark the [LCP image](https://web.dev/lcp/#what-elements-are-considered) on your page as `priority` to prioritize its loading.
-
+```html
-
+```
Marking an image as `priority` applies the following optimizations:
-* Sets `fetchpriority=high` (read more about priority hints [here](https://web.dev/priority-hints))
-* Sets `loading=eager` (read more about native lazy loading [here](https://web.dev/browser-level-image-lazy-loading))
-* Automatically generates a [preload link element](https://developer.mozilla.org/docs/Web/HTML/Link_types/preload) if [rendering on the server](guide/ssr).
+- Sets `fetchpriority=high` (read more about priority hints [here](https://web.dev/priority-hints))
+- Sets `loading=eager` (read more about native lazy loading [here](https://web.dev/browser-level-image-lazy-loading))
+- Automatically generates a [preload link element](https://developer.mozilla.org/docs/Web/HTML/Link_types/preload) if [rendering on the server](guide/ssr).
Angular displays a warning during development if the LCP element is an image that does not have the `priority` attribute. A page’s LCP element can vary based on a number of factors - such as the dimensions of a user's screen, so a page may have multiple images that should be marked `priority`. See [CSS for Web Vitals](https://web.dev/css-web-vitals/#images-and-largest-contentful-paint-lcp) for more details.
In order to prevent [image-related layout shifts](https://web.dev/css-web-vitals/#images-and-layout-shifts), NgOptimizedImage requires that you specify a height and width for your image, as follows:
-
+```html
-
+```
For **responsive images** (images which you've styled to grow and shrink relative to the viewport), the `width` and `height` attributes should be the intrinsic size of the image file. For responsive images it's also important to [set a value for `sizes`.](#responsive-images)
@@ -100,11 +100,11 @@ In cases where you want to have an image fill a containing element, you can use
When you add the `fill` attribute to your image, you do not need and should not include a `width` and `height`, as in this example:
-
+```html
-
+```
You can use the [object-fit](https://developer.mozilla.org/docs/Web/CSS/object-fit) CSS property to change how the image will fill its container. If you style your image with `object-fit: "contain"`, the image will maintain its aspect ratio and be "letterboxed" to fit the element. If you set `object-fit: "cover"`, the element will retain its aspect ratio, fully fill the element, and some content may be "cropped" off.
@@ -118,32 +118,31 @@ IMPORTANT: For the "fill" image to render properly, its parent element **must**
Here's a simple step-by-step process for migrating from `background-image` to `NgOptimizedImage`. For these steps, we'll refer to the element that has an image background as the "containing element":
-1) Remove the `background-image` style from the containing element.
-2) Ensure that the containing element has `position: "relative"`, `position: "fixed"`, or `position: "absolute"`.
-3) Create a new image element as a child of the containing element, using `ngSrc` to enable the `NgOptimizedImage` directive.
-4) Give that element the `fill` attribute. Do not include a `height` and `width`.
-5) If you believe this image might be your [LCP element](https://web.dev/lcp/), add the `priority` attribute to the image element.
+1. Remove the `background-image` style from the containing element.
+2. Ensure that the containing element has `position: "relative"`, `position: "fixed"`, or `position: "absolute"`.
+3. Create a new image element as a child of the containing element, using `ngSrc` to enable the `NgOptimizedImage` directive.
+4. Give that element the `fill` attribute. Do not include a `height` and `width`.
+5. If you believe this image might be your [LCP element](https://web.dev/lcp/), add the `priority` attribute to the image element.
You can adjust how the background image fills the container as described in the [Using fill mode](#using-fill-mode) section.
-
## Using placeholders
### Automatic placeholders
NgOptimizedImage can display an automatic low-resolution placeholder for your image if you're using a CDN or image host that provides automatic image resizing. Take advantage of this feature by adding the `placeholder` attribute to your image:
-
+```html
-
+```
Adding this attribute automatically requests a second, smaller version of the image using your specified image loader. This small image will be applied as a `background-image` style with a CSS blur while your image loads. If no image loader is provided, no placeholder image can be generated and an error will be thrown.
The default size for generated placeholders is 30px wide. You can change this size by specifying a pixel value in the `IMAGE_CONFIG` provider, as seen below:
-
+```ts
providers: [
{
provide: IMAGE_CONFIG,
@@ -152,7 +151,7 @@ providers: [
}
},
],
-
+```
If you want sharp edges around your blurred placeholder, you can wrap your image in a containing `
` with the `overflow: hidden` style. As long as the `
` is the same size as the image (such as by using the `width: fit-content` style), the "fuzzy edges" of the placeholder will be hidden.
@@ -160,7 +159,7 @@ If you want sharp edges around your blurred placeholder, you can wrap your image
You can also specify a placeholder using a base64 [data URL](https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URLs) without an image loader. The data url format is `data:image/[imagetype];[data]`, where `[imagetype]` is the image format, just as `png`, and `[data]` is a base64 encoding of the image. That encoding can be done using the command line or in JavaScript. For specific commands, see [the MDN documentation](https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URLs#encoding_data_into_base64_format). An example of a data URL placeholder with truncated data is shown below:
-
+```html
-
+```
-However, large data URLs increase the size of your Angular bundles and slow down page load. If you cannot use an image loader, the Angular team recommends keeping base64 placeholder images smaller than 4KB and using them exclusively on critical images. In addition to decreasing placeholder dimensions, consider changing image formats or parameters used when saving images. At very low resolutions, these parameters can have a large effect on file size.
+However, large data URLs increase the size of your Angular bundles and slow down page load. If you cannot use an image loader, the Angular team recommends keeping base64 placeholder images smaller than 4KB and using them exclusively on critical images. In addition to decreasing placeholder dimensions, consider changing image formats or parameters used when saving images. At very low resolutions, these parameters can have a large effect on file size.
### Non-blurred placeholders
By default, NgOptimizedImage applies a CSS blur effect to image placeholders. To render a placeholder without blur, provide a `placeholderConfig` argument with an object that includes the `blur` property, set to false. For example:
-
-
+```html
-
-
+```
## Adjusting image styling
@@ -207,36 +204,35 @@ A [`preconnect` resource hint](https://web.dev/preconnect-and-dns-prefetch) for
Preconnect links are automatically generated for domains provided as an argument to a [loader](#optional-set-up-a-loader). If an image origin cannot be automatically identified, and no preconnect link is detected for the LCP image, `NgOptimizedImage` will warn during development. In that case, you should manually add a resource hint to `index.html`. Within the `` of the document, add a `link` tag with `rel="preconnect"`, as shown below:
-
+```html
-
-
+```
To disable preconnect warnings, inject the `PRECONNECT_CHECK_BLOCKLIST` token:
-
+```ts
providers: [
- {provide: PRECONNECT_CHECK_BLOCKLIST, useValue: 'https://your-domain.com'}
+{provide: PRECONNECT_CHECK_BLOCKLIST, useValue: 'https://your-domain.com'}
],
-
+```
See more information on automatic preconnect generation [here](#why-is-a-preconnect-element-not-being-generated-for-my-image-domain).
-
### Request images at the correct size with automatic `srcset`
Defining a [`srcset` attribute](https://developer.mozilla.org/docs/Web/API/HTMLImageElement/srcset) ensures that the browser requests an image at the right size for your user's viewport, so it doesn't waste time downloading an image that's too large. `NgOptimizedImage` generates an appropriate `srcset` for the image, based on the presence and value of the [`sizes` attribute](https://developer.mozilla.org/docs/Web/API/HTMLImageElement/sizes) on the image tag.
#### Fixed-size images
-If your image should be "fixed" in size (i.e. the same size across devices, except for [pixel density](https://web.dev/codelab-density-descriptors/)), there is no need to set a `sizes` attribute. A `srcset` can be generated automatically from the image's width and height attributes with no further input required.
+If your image should be "fixed" in size (i.e. the same size across devices, except for [pixel density](https://web.dev/codelab-density-descriptors/)), there is no need to set a `sizes` attribute. A `srcset` can be generated automatically from the image's width and height attributes with no further input required.
Example srcset generated:
-```angular-html
+
+```html
```
@@ -256,7 +252,7 @@ By default, the responsive breakpoints are:
If you would like to customize these breakpoints, you can do so using the `IMAGE_CONFIG` provider:
-
+```ts
providers: [
{
provide: IMAGE_CONFIG,
@@ -265,53 +261,80 @@ providers: [
}
},
],
-
+```
If you would like to manually define a `srcset` attribute, you can provide your own using the `ngSrcset` attribute:
-
+```html
-
+```
If the `ngSrcset` attribute is present, `NgOptimizedImage` generates and sets the `srcset` based on the sizes included. Do not include image file names in `ngSrcset` - the directive infers this information from `ngSrc`. The directive supports both width descriptors (e.g. `100w`) and density descriptors (e.g. `1x`).
-
+```html
-
+```
### Disabling automatic srcset generation
To disable srcset generation for a single image, you can add the `disableOptimizedSrcset` attribute on the image:
-
+```html
-
+```
### Disabling image lazy loading
By default, `NgOptimizedImage` sets `loading=lazy` for all images that are not marked `priority`. You can disable this behavior for non-priority images by setting the `loading` attribute. This attribute accepts values: `eager`, `auto`, and `lazy`. [See the documentation for the standard image `loading` attribute for details](https://developer.mozilla.org/docs/Web/API/HTMLImageElement/loading#value).
-
+```html
-
+```
+
+### Controlling image decoding
+
+By default, `NgOptimizedImage` sets `decoding="auto"` for all images. This allows the browser to decide the optimal time to decode an image after it has been fetched. When an image is marked as `priority`, Angular automatically sets `decoding="sync"` to ensure the image is decoded and painted as early as possible helping improve **Largest Contentful Paint (LCP)** performance.
+
+You can still override this behavior by explicitly setting the `decoding` attribute.
+[See the documentation for the standard image `decoding` attribute for details](https://developer.mozilla.org/docs/Web/HTML/Element/img#decoding).
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+```
+
+**Allowed values**
+
+- `auto` (default): lets the browser choose the optimal strategy.
+- `async`: decodes the image asynchronously, avoiding main‑thread blocking where possible.
+- `sync`: decodes the image immediately; can block rendering but ensures pixels are ready as soon as the image is available.
### Advanced 'sizes' values
You may want to have images displayed at varying widths on differently-sized screens. A common example of this pattern is a grid- or column-based layout that renders a single column on mobile devices, and two columns on larger devices. You can capture this behavior in the `sizes` attribute, using a "media query" syntax, such as the following:
-
+```html
-
+```
The `sizes` attribute in the above example says "I expect this image to be 100 percent of the screen width on devices under 768px wide. Otherwise, I expect it to be 50 percent of the screen width.
@@ -323,21 +346,21 @@ A "loader" is a function that generates an [image transformation URL](https://we
`NgOptimizedImage` provides both a generic loader that applies no transformations, as well as loaders for various third-party image services. It also supports writing your own custom loader.
-| Loader type| Behavior |
-|:--- |:--- |
-| Generic loader | The URL returned by the generic loader will always match the value of `src`. In other words, this loader applies no transformations. Sites that use Angular to serve images are the primary intended use case for this loader.|
-| Loaders for third-party image services | The URL returned by the loaders for third-party image services will follow API conventions used by that particular image service. |
-| Custom loaders | A custom loader's behavior is defined by its developer. You should use a custom loader if your image service isn't supported by the loaders that come preconfigured with `NgOptimizedImage`.|
+| Loader type | Behavior |
+| :------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Generic loader | The URL returned by the generic loader will always match the value of `src`. In other words, this loader applies no transformations. Sites that use Angular to serve images are the primary intended use case for this loader. |
+| Loaders for third-party image services | The URL returned by the loaders for third-party image services will follow API conventions used by that particular image service. |
+| Custom loaders | A custom loader's behavior is defined by its developer. You should use a custom loader if your image service isn't supported by the loaders that come preconfigured with `NgOptimizedImage`. |
Based on the image services commonly used with Angular applications, `NgOptimizedImage` provides loaders preconfigured to work with the following image services:
-| Image Service | Angular API | Documentation |
-|:--- |:--- |:--- |
-| Cloudflare Image Resizing | `provideCloudflareLoader` | [Documentation](https://developers.cloudflare.com/images/image-resizing/) |
-| Cloudinary | `provideCloudinaryLoader` | [Documentation](https://cloudinary.com/documentation/resizing_and_cropping) |
-| ImageKit | `provideImageKitLoader` | [Documentation](https://docs.imagekit.io/) |
-| Imgix | `provideImgixLoader` | [Documentation](https://docs.imgix.com/) |
-| Netlify | `provideNetlifyLoader` | [Documentation](https://docs.netlify.com/image-cdn/overview/) |
+| Image Service | Angular API | Documentation |
+| :------------------------ | :------------------------ | :-------------------------------------------------------------------------- |
+| Cloudflare Image Resizing | `provideCloudflareLoader` | [Documentation](https://developers.cloudflare.com/images/image-resizing/) |
+| Cloudinary | `provideCloudinaryLoader` | [Documentation](https://cloudinary.com/documentation/resizing_and_cropping) |
+| ImageKit | `provideImageKitLoader` | [Documentation](https://docs.imagekit.io/) |
+| Imgix | `provideImgixLoader` | [Documentation](https://docs.imgix.com/) |
+| Netlify | `provideNetlifyLoader` | [Documentation](https://docs.netlify.com/image-cdn/overview/) |
To use the **generic loader** no additional code changes are necessary. This is the default behavior.
@@ -345,17 +368,17 @@ To use the **generic loader** no additional code changes are necessary. This is
To use an existing loader for a **third-party image service**, add the provider factory for your chosen service to the `providers` array. In the example below, the Imgix loader is used:
-
+```ts
providers: [
provideImgixLoader('https://my.base.url/'),
],
-
+```
The base URL for your image assets should be passed to the provider factory as an argument. For most sites, this base URL should match one of the following patterns:
-*
-*
-*
+-
+-
+-
You can learn more about the base URL structure in the docs of a corresponding CDN provider.
@@ -363,7 +386,7 @@ You can learn more about the base URL structure in the docs of a corresponding C
To use a **custom loader**, provide your loader function as a value for the `IMAGE_LOADER` DI token. In the example below, the custom loader function returns a URL starting with `https://example.com` that includes `src` and `width` as URL parameters.
-
+```ts
providers: [
{
provide: IMAGE_LOADER,
@@ -372,7 +395,7 @@ providers: [
},
},
],
-
+```
A loader function for the `NgOptimizedImage` directive takes an object with the `ImageLoaderConfig` type (from `@angular/common`) as its argument and returns the absolute URL of the image asset. The `ImageLoaderConfig` object contains the `src` property, and optional `width` and `loaderParams` properties.
@@ -388,7 +411,7 @@ A common use for `loaderParams` is controlling advanced image CDN features.
The following shows an example of a custom loader function. This example function concatenates `src` and `width`, and uses `loaderParams` to control a custom CDN feature for rounded corners:
-
+```ts
const myCustomLoader = (config: ImageLoaderConfig) => {
let url = `https://example.com/images/${config.src}?`;
let queryParams = [];
@@ -400,15 +423,15 @@ const myCustomLoader = (config: ImageLoaderConfig) => {
}
return url + queryParams.join('&');
};
-
+```
Note that in the above example, we've invented the 'roundedCorners' property name to control a feature of our custom loader. We could then use this feature when creating an image, as follows:
-
+```html
-
+```
## Frequently Asked Questions
@@ -423,17 +446,17 @@ For a step-by-step process for migration from `background-image` to `NgOptimized
The `ngSrc` attribute was chosen as the trigger for NgOptimizedImage due to technical considerations around how images are loaded by the browser. NgOptimizedImage makes programmatic changes to the `loading` attribute -- if the browser sees the `src` attribute before those changes are made, it will begin eagerly downloading the image file, and the loading changes will be ignored.
### Why is a preconnect element not being generated for my image domain?
+
Preconnect generation is performed based on static analysis of your application. That means that the image domain must be directly included in the loader parameter, as in the following example:
-
+```ts
providers: [
provideImgixLoader('https://my.base.url/'),
],
-
+```
If you use a variable to pass the domain string to the loader, or you're not using a loader, the static analysis will not be able to identify the domain, and no preconnect link will be generated. In this case you should manually add a preconnect link to the document head, as [described above](#add-resource-hints).
-
### Can I use two different image domains in the same page?
The [image loaders](#configuring-an-image-loader-for-ngoptimizedimage) provider pattern is designed to be as simple as possible for the common use case of having only a single image CDN used within a component. However, it's still very possible to manage multiple image CDNs using a single provider.
@@ -458,7 +481,7 @@ If you're waiting on this feature, please upvote the Github issue [here](https:/
3. Once the profiling result is available, select "LCP" in the timings section.
-4. A summary entry should appear in the panel at the bottom. You can find the LCP element in the row for "related node". Clicking on it will reveal the element in the Elements panel.
+4. A summary entry should appear in the panel at the bottom. You can find the LCP element in the row for "related node". Clicking on it will reveal the element in the Elements panel.
diff --git a/adev/src/content/guide/routing/customizing-route-behavior.md b/adev/src/content/guide/routing/customizing-route-behavior.md
index 6ae475fc59d..94ac76f94d2 100644
--- a/adev/src/content/guide/routing/customizing-route-behavior.md
+++ b/adev/src/content/guide/routing/customizing-route-behavior.md
@@ -339,7 +339,7 @@ export const routes: Routes = [
The component receives the extracted parameters through route inputs:
-```ts
+```angular-ts
import { Component, input, inject } from '@angular/core';
import { resource } from '@angular/core';
diff --git a/adev/src/content/guide/routing/route-guards.md b/adev/src/content/guide/routing/route-guards.md
index b25e9eb480e..774e5f91296 100644
--- a/adev/src/content/guide/routing/route-guards.md
+++ b/adev/src/content/guide/routing/route-guards.md
@@ -187,7 +187,7 @@ const routes: Routes = [
children: [
// /users/list - PROTECTED
{ path: 'list', component: UserListComponent },
- // /useres/detail/:id - PROTECTED
+ // /users/detail/:id - PROTECTED
{ path: 'detail/:id', component: UserDetailComponent }
]
},
diff --git a/adev/src/content/guide/security.md b/adev/src/content/guide/security.md
index 6ad5dffac34..212aa4fac73 100644
--- a/adev/src/content/guide/security.md
+++ b/adev/src/content/guide/security.md
@@ -51,7 +51,7 @@ For this reason, it is strongly encouraged to take advantage of these features.
### Sanitization and security contexts
-*Sanitization* is the inspection of an untrusted value, turning it into a value that's safe to insert into the DOM.
+_Sanitization_ is the inspection of an untrusted value, turning it into a value that's safe to insert into the DOM.
In many cases, sanitization doesn't change a value at all.
Sanitization depends on a context.
For example, a value that's harmless in CSS is potentially dangerous in a URL.
@@ -107,11 +107,11 @@ If in doubt, find a professional security reviewer.
To mark a value as trusted, inject `DomSanitizer` and call one of the following methods:
-* `bypassSecurityTrustHtml`
-* `bypassSecurityTrustScript`
-* `bypassSecurityTrustStyle`
-* `bypassSecurityTrustUrl`
-* `bypassSecurityTrustResourceUrl`
+- `bypassSecurityTrustHtml`
+- `bypassSecurityTrustScript`
+- `bypassSecurityTrustStyle`
+- `bypassSecurityTrustUrl`
+- `bypassSecurityTrustResourceUrl`
Remember, whether a value is safe depends on context, so choose the right context for your intended use of the value.
Imagine that the following template needs to bind a URL to a `javascript:alert(...)` call:
@@ -142,21 +142,19 @@ Read more about content security policy at the [Web Fundamentals guide](https://
The minimal policy required for a brand-new Angular application is:
-
-
+```txt
default-src 'self'; style-src 'self' 'nonce-randomNonceGoesHere'; script-src 'self' 'nonce-randomNonceGoesHere';
+```
-
-
-When serving your Angular application, the server should include a randomly-generated nonce in the HTTP header for each request.
+When serving your Angular application, the server should include a randomly-generated nonce in the HTTP header for each request.
You must provide this nonce to Angular so that the framework can render `