Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions guides/creating-a-custom-form-field-control.md
Original file line number Diff line number Diff line change
@@ -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: `
<mat-form-field>
<mat-label>Address line</mat-label>
<input matInput [formControl]="control" />
@if (control.hasError('required')) {
<mat-error>This field is required</mat-error>
}
</mat-form-field>
`,
})
export class AddressLine {
@Input({required: true}) control!: FormControl<string>;
}
```

Use the wrapper with the control from the parent form:

```html
<app-address-line [control]="addressForm.controls.street" />
```

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 `<mat-form-field>` 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 `<mat-form-field>`. 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.
Expand Down
24 changes: 24 additions & 0 deletions src/cdk/scrolling/scrolling.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down