Skip to content

Commit

Permalink
refactor(angular/input): support signal-based input accessor (#2394)
Browse files Browse the repository at this point in the history
Expands the `SBB_INPUT_VALUE_ACCESSOR` to support a signal-based accessor.
  • Loading branch information
mhaertwig authored Oct 3, 2024
1 parent 2ac4a80 commit 2d675e5
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/angular/input/input-value-accessor.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { InjectionToken } from '@angular/core';
import { InjectionToken, WritableSignal } from '@angular/core';

/**
* This token is used to inject the object whose value should be set into `InputDirective`.
* If none is provided, the native `HTMLInputElement` is used. Directives can provide
* themselves for this token, in order to make `InputDirective` delegate the getting and setting of the
* value to them.
*/
export const SBB_INPUT_VALUE_ACCESSOR = new InjectionToken<{ value: any }>(
export const SBB_INPUT_VALUE_ACCESSOR = new InjectionToken<{ value: any | WritableSignal<any> }>(
'SBB_INPUT_VALUE_ACCESSOR',
);
38 changes: 32 additions & 6 deletions src/angular/input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import {
AfterViewInit,
Directive,
DoCheck,
effect,
ElementRef,
HostListener,
inject,
Input,
isSignal,
NgZone,
numberAttribute,
OnChanges,
OnDestroy,
WritableSignal,
} from '@angular/core';
import { FormGroupDirective, NgControl, NgForm } from '@angular/forms';
import { SbbErrorStateMatcher, _ErrorStateTracker } from '@sbb-esta/angular/core';
Expand Down Expand Up @@ -68,6 +71,7 @@ export class SbbInput
private _autofillMonitor = inject(AutofillMonitor);
private _previousNativeValue: any;
private _inputValueAccessor: { value: any };
private _signalBasedValueAccessor?: { value: WritableSignal<any> };
private _errorStateTracker: _ErrorStateTracker;
ngControl: NgControl = inject(NgControl, { optional: true, self: true })!;

Expand Down Expand Up @@ -194,13 +198,19 @@ export class SbbInput
*/
@Input()
get value(): string {
return this._inputValueAccessor.value;
return this._signalBasedValueAccessor
? this._signalBasedValueAccessor.value()
: this._inputValueAccessor.value;
}
// Accept `any` to avoid conflicts with other directives on `<input>` that may
// accept different types.
set value(value: any) {
if (value !== this.value) {
this._inputValueAccessor.value = value;
if (this._signalBasedValueAccessor) {
this._signalBasedValueAccessor.value.set(value);
} else {
this._inputValueAccessor.value = value;
}
this.stateChanges.next();
}
}
Expand Down Expand Up @@ -255,15 +265,23 @@ export class SbbInput
const parentForm = inject(NgForm, { optional: true })!;
const parentFormGroup = inject(FormGroupDirective, { optional: true })!;
const defaultErrorStateMatcher = inject(SbbErrorStateMatcher);
const inputValueAccessor = inject(SBB_INPUT_VALUE_ACCESSOR, { optional: true, self: true })!;
const accessor = inject(SBB_INPUT_VALUE_ACCESSOR, { optional: true, self: true })!;
const ngZone = inject(NgZone);

const element = this._elementRef.nativeElement;
const nodeName = element.nodeName.toLowerCase();

// If no input value accessor was explicitly specified, use the element as the input value
// accessor.
this._inputValueAccessor = inputValueAccessor || element;
if (accessor) {
if (isSignal(accessor.value)) {
this._signalBasedValueAccessor = accessor;
} else {
this._inputValueAccessor = accessor;
}
} else {
// If no input value accessor was explicitly specified, use the element as the input value
// accessor.
this._inputValueAccessor = element;
}

this._previousNativeValue = this.value;

Expand All @@ -290,6 +308,14 @@ export class SbbInput
? 'sbb-native-select-multiple'
: 'sbb-native-select';
}

if (this._signalBasedValueAccessor) {
effect(() => {
// Read the value so the effect can register the dependency.
this._signalBasedValueAccessor!.value();
this.stateChanges.next();
});
}
}

ngAfterViewInit() {
Expand Down

0 comments on commit 2d675e5

Please sign in to comment.