@@ -14,13 +14,16 @@ import {
14
14
booleanAttribute ,
15
15
Directive ,
16
16
DoCheck ,
17
+ effect ,
17
18
ElementRef ,
18
19
inject ,
19
20
InjectionToken ,
20
21
Input ,
22
+ isSignal ,
21
23
NgZone ,
22
24
OnChanges ,
23
25
OnDestroy ,
26
+ WritableSignal ,
24
27
} from '@angular/core' ;
25
28
import { FormGroupDirective , NgControl , NgForm , Validators } from '@angular/forms' ;
26
29
import { ErrorStateMatcher , _ErrorStateTracker } from '@angular/material/core' ;
@@ -104,6 +107,7 @@ export class MatInput
104
107
protected _uid = `mat-input-${ nextUniqueId ++ } ` ;
105
108
protected _previousNativeValue : any ;
106
109
private _inputValueAccessor : { value : any } ;
110
+ private _signalBasedValueAccessor ?: { value : WritableSignal < any > } ;
107
111
private _previousPlaceholder : string | null ;
108
112
private _errorStateTracker : _ErrorStateTracker ;
109
113
private _webkitBlinkWheelListenerAttached = false ;
@@ -244,11 +248,18 @@ export class MatInput
244
248
*/
245
249
@Input ( )
246
250
get value ( ) : string {
247
- return this . _inputValueAccessor . value ;
251
+ return this . _signalBasedValueAccessor
252
+ ? this . _signalBasedValueAccessor . value ( )
253
+ : this . _inputValueAccessor . value ;
248
254
}
249
255
set value ( value : any ) {
250
256
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
+
252
263
this . stateChanges . next ( ) ;
253
264
}
254
265
}
@@ -290,14 +301,22 @@ export class MatInput
290
301
const parentForm = inject ( NgForm , { optional : true } ) ;
291
302
const parentFormGroup = inject ( FormGroupDirective , { optional : true } ) ;
292
303
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 } ) ;
294
305
295
306
const element = this . _elementRef . nativeElement ;
296
307
const nodeName = element . nodeName . toLowerCase ( ) ;
297
308
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
+ }
301
320
302
321
this . _previousNativeValue = this . value ;
303
322
@@ -331,6 +350,14 @@ export class MatInput
331
350
? 'mat-native-select-multiple'
332
351
: 'mat-native-select' ;
333
352
}
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
+ }
334
361
}
335
362
336
363
ngAfterViewInit ( ) {
0 commit comments