@@ -112,6 +112,61 @@ describe('field directive', () => {
112112 } ) ;
113113
114114 describe ( 'properties' , ( ) => {
115+ describe ( 'dirty' , ( ) => {
116+ it ( 'should bind to custom control' , ( ) => {
117+ @Component ( {
118+ selector : 'custom-control' ,
119+ template : '<input #i [value]="value()" (input)="value.set(i.value)" />' ,
120+ } )
121+ class CustomControl implements FormValueControl < string > {
122+ readonly value = model . required < string > ( ) ;
123+ readonly dirty = input . required < boolean > ( ) ;
124+ }
125+
126+ @Component ( {
127+ template : ` <custom-control [field]="f" /> ` ,
128+ imports : [ CustomControl , Field ] ,
129+ } )
130+ class TestCmp {
131+ readonly data = signal ( '' ) ;
132+ readonly f = form ( this . data ) ;
133+ readonly customControl = viewChild . required ( CustomControl ) ;
134+ }
135+
136+ const comp = act ( ( ) => TestBed . createComponent ( TestCmp ) ) . componentInstance ;
137+ expect ( comp . customControl ( ) . dirty ( ) ) . toBe ( false ) ;
138+ act ( ( ) => comp . f ( ) . markAsDirty ( ) ) ;
139+ expect ( comp . customControl ( ) . dirty ( ) ) . toBe ( true ) ;
140+ } ) ;
141+
142+ it ( 'should be reset when field changes on custom control' , ( ) => {
143+ @Component ( { selector : 'custom-control' , template : `` } )
144+ class CustomControl implements FormValueControl < string > {
145+ readonly value = model . required < string > ( ) ;
146+ readonly dirty = input . required < boolean > ( ) ;
147+ }
148+
149+ @Component ( {
150+ imports : [ Field , CustomControl ] ,
151+ template : `<custom-control [field]="field()" />` ,
152+ } )
153+ class TestCmp {
154+ readonly f = form ( signal ( { x : '' , y : '' } ) ) ;
155+ readonly field = signal ( this . f . x ) ;
156+ readonly customControl = viewChild . required ( CustomControl ) ;
157+ }
158+
159+ const fixture = act ( ( ) => TestBed . createComponent ( TestCmp ) ) ;
160+ const component = fixture . componentInstance ;
161+
162+ act ( ( ) => component . f . x ( ) . markAsDirty ( ) ) ;
163+ expect ( component . customControl ( ) . dirty ( ) ) . toBe ( true ) ;
164+
165+ act ( ( ) => component . field . set ( component . f . y ) ) ;
166+ expect ( component . customControl ( ) . dirty ( ) ) . toBe ( false ) ;
167+ } ) ;
168+ } ) ;
169+
115170 describe ( 'disabled' , ( ) => {
116171 it ( 'should bind to native control' , ( ) => {
117172 @Component ( {
@@ -2123,32 +2178,6 @@ describe('field directive', () => {
21232178 ] ) ;
21242179 } ) ;
21252180
2126- it ( 'should synchronize dirty status' , ( ) => {
2127- @Component ( {
2128- selector : 'my-input' ,
2129- template : '<input #i [value]="value()" (input)="value.set(i.value)" />' ,
2130- } )
2131- class CustomInput implements FormValueControl < string > {
2132- value = model ( '' ) ;
2133- dirty = input ( false ) ;
2134- }
2135-
2136- @Component ( {
2137- template : ` <my-input [field]="f" /> ` ,
2138- imports : [ CustomInput , Field ] ,
2139- } )
2140- class DirtyTestCmp {
2141- myInput = viewChild . required < CustomInput > ( CustomInput ) ;
2142- data = signal ( '' ) ;
2143- f = form ( this . data ) ;
2144- }
2145-
2146- const comp = act ( ( ) => TestBed . createComponent ( DirtyTestCmp ) ) . componentInstance ;
2147- expect ( comp . myInput ( ) . dirty ( ) ) . toBe ( false ) ;
2148- act ( ( ) => comp . f ( ) . markAsDirty ( ) ) ;
2149- expect ( comp . myInput ( ) . dirty ( ) ) . toBe ( true ) ;
2150- } ) ;
2151-
21522181 it ( 'should synchronize pending status' , async ( ) => {
21532182 const { promise, resolve} = promiseWithResolvers < ValidationError [ ] > ( ) ;
21542183
0 commit comments