@@ -14,13 +14,16 @@ import {
1414 booleanAttribute ,
1515 Directive ,
1616 DoCheck ,
17+ effect ,
1718 ElementRef ,
1819 inject ,
1920 InjectionToken ,
2021 Input ,
22+ isSignal ,
2123 NgZone ,
2224 OnChanges ,
2325 OnDestroy ,
26+ WritableSignal ,
2427} from '@angular/core' ;
2528import { FormGroupDirective , NgControl , NgForm , Validators } from '@angular/forms' ;
2629import { ErrorStateMatcher , _ErrorStateTracker } from '@angular/material/core' ;
@@ -104,6 +107,7 @@ export class MatInput
104107 protected _uid = `mat-input-${ nextUniqueId ++ } ` ;
105108 protected _previousNativeValue : any ;
106109 private _inputValueAccessor : { value : any } ;
110+ private _signalBasedValueAccessor ?: { value : WritableSignal < any > } ;
107111 private _previousPlaceholder : string | null ;
108112 private _errorStateTracker : _ErrorStateTracker ;
109113 private _webkitBlinkWheelListenerAttached = false ;
@@ -244,11 +248,18 @@ export class MatInput
244248 */
245249 @Input ( )
246250 get value ( ) : string {
247- return this . _inputValueAccessor . value ;
251+ return this . _signalBasedValueAccessor
252+ ? this . _signalBasedValueAccessor . value ( )
253+ : this . _inputValueAccessor . value ;
248254 }
249255 set value ( value : any ) {
250256 if ( value !== this . value ) {
251- this . _inputValueAccessor . value = value ;
257+ if ( this . _signalBasedValueAccessor ) {
258+ this . _signalBasedValueAccessor . value . set ( value ) ;
259+ } else {
260+ this . _inputValueAccessor . value = value ;
261+ }
262+
252263 this . stateChanges . next ( ) ;
253264 }
254265 }
@@ -290,14 +301,22 @@ export class MatInput
290301 const parentForm = inject ( NgForm , { optional : true } ) ;
291302 const parentFormGroup = inject ( FormGroupDirective , { optional : true } ) ;
292303 const defaultErrorStateMatcher = inject ( ErrorStateMatcher ) ;
293- const inputValueAccessor = inject ( MAT_INPUT_VALUE_ACCESSOR , { optional : true , self : true } ) ;
304+ const accessor = inject ( MAT_INPUT_VALUE_ACCESSOR , { optional : true , self : true } ) ;
294305
295306 const element = this . _elementRef . nativeElement ;
296307 const nodeName = element . nodeName . toLowerCase ( ) ;
297308
298- // If no input value accessor was explicitly specified, use the element as the input value
299- // accessor.
300- this . _inputValueAccessor = inputValueAccessor || element ;
309+ if ( accessor ) {
310+ if ( isSignal ( accessor . value ) ) {
311+ this . _signalBasedValueAccessor = accessor ;
312+ } else {
313+ this . _inputValueAccessor = accessor ;
314+ }
315+ } else {
316+ // If no input value accessor was explicitly specified, use the element as the input value
317+ // accessor.
318+ this . _inputValueAccessor = element ;
319+ }
301320
302321 this . _previousNativeValue = this . value ;
303322
@@ -331,6 +350,14 @@ export class MatInput
331350 ? 'mat-native-select-multiple'
332351 : 'mat-native-select' ;
333352 }
353+
354+ if ( this . _signalBasedValueAccessor ) {
355+ effect ( ( ) => {
356+ // Read the value so the effect can register the dependency.
357+ this . _signalBasedValueAccessor ! . value ( ) ;
358+ this . stateChanges . next ( ) ;
359+ } ) ;
360+ }
334361 }
335362
336363 ngAfterViewInit ( ) {
0 commit comments