diff --git a/guides/creating-a-custom-form-field-control.md b/guides/creating-a-custom-form-field-control.md index 947e54f24a13..facafb0dc899 100644 --- a/guides/creating-a-custom-form-field-control.md +++ b/guides/creating-a-custom-form-field-control.md @@ -1,5 +1,46 @@ # Creating a custom form field control +## Wrapping an existing Material control + +If a reusable component only needs to configure an existing Material control, keep the form +control on the native Material element instead of implementing `MatFormFieldControl` again. Pass +the control to the wrapper and bind it with `formControl`: + +```ts +import {Component, Input} from '@angular/core'; +import {FormControl, ReactiveFormsModule} from '@angular/forms'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatInputModule} from '@angular/material/input'; + +@Component({ + selector: 'app-address-line', + imports: [MatFormFieldModule, MatInputModule, ReactiveFormsModule], + template: ` + + Address line + + @if (control.hasError('required')) { + This field is required + } + + `, +}) +export class AddressLine { + @Input({required: true}) control!: FormControl; +} +``` + +Use the wrapper with the control from the parent form: + +```html + +``` + +This pattern preserves the control's value, validation, touched state, and error-state handling. +Use the custom form field control pattern below when the component itself must behave as the direct +child of `` or needs to expose a value that is not represented by an existing +Material control. + It is possible to create custom form field controls that can be used inside ``. This can be useful if you need to create a component that shares a lot of common behavior with a form field, but adds some additional logic. diff --git a/src/cdk/scrolling/scrolling.md b/src/cdk/scrolling/scrolling.md index a1bec9184880..92c4963c3e98 100644 --- a/src/cdk/scrolling/scrolling.md +++ b/src/cdk/scrolling/scrolling.md @@ -9,6 +9,30 @@ This marks the element as a `Scrollable` and registers it with the `ScrollDispat dispatcher, then, allows components to share both event listeners and knowledge of all of the scrollable containers in the application. +#### Reacting to a scrollable container + +Inject `ScrollDispatcher` when a component needs to react to scrolling anywhere in the application. +The `scrolled` observable emits the `CdkScrollable` that caused the event, or `undefined` when the +event came from the document. Use the optional audit time when the handler does not need to run for +every native scroll event. + +```ts +import {CdkScrollable, ScrollDispatcher} from '@angular/cdk/scrolling'; + +export class ScrollSpy { + constructor(scrollDispatcher: ScrollDispatcher) { + scrollDispatcher.scrolled(100).subscribe((scrollable: CdkScrollable | undefined) => { + const element = scrollable?.getElementRef().nativeElement; + // Update the active section using element?.scrollTop or the document scroll position. + }); + } +} +``` + +For a single container, inject `CdkScrollable` and subscribe to its `elementScrolled()` observable +instead. The container must have the `cdkScrollable` directive, and the subscription should be +cleaned up with the component's lifecycle. + ### ViewportRuler The `ViewportRuler` is a service that can be injected and used to measure the bounds of the browser viewport.