@@ -545,6 +545,97 @@ describe('field directive', () => {
545545 } ) ;
546546 } ) ;
547547
548+ describe ( 'pending' , ( ) => {
549+ it ( 'should bind to custom control' , async ( ) => {
550+ const { promise, resolve} = promiseWithResolvers < ValidationError [ ] > ( ) ;
551+
552+ @Component ( {
553+ selector : 'custom-control' ,
554+ template : '<input #i [value]="value()" (input)="value.set(i.value)" />' ,
555+ } )
556+ class CustomControl implements FormValueControl < string > {
557+ readonly value = model . required < string > ( ) ;
558+ readonly pending = input . required < boolean > ( ) ;
559+ }
560+
561+ @Component ( {
562+ template : ` <custom-control [field]="f" /> ` ,
563+ imports : [ CustomControl , Field ] ,
564+ } )
565+ class TestCmp {
566+ readonly data = signal ( 'test' ) ;
567+ readonly f = form ( this . data , ( p ) => {
568+ validateAsync ( p , {
569+ params : ( ) => [ ] ,
570+ factory : ( params ) =>
571+ resource ( {
572+ params,
573+ loader : ( ) => promise ,
574+ } ) ,
575+ onSuccess : ( results ) => results ,
576+ onError : ( ) => null ,
577+ } ) ;
578+ } ) ;
579+ readonly customControl = viewChild . required ( CustomControl ) ;
580+ }
581+
582+ const fixture = act ( ( ) => TestBed . createComponent ( TestCmp ) ) ;
583+ const comp = fixture . componentInstance ;
584+
585+ expect ( comp . customControl ( ) . pending ( ) ) . toBe ( true ) ;
586+
587+ resolve ( [ ] ) ;
588+ await promise ;
589+ await fixture . whenStable ( ) ;
590+
591+ expect ( comp . customControl ( ) . pending ( ) ) . toBe ( false ) ;
592+ } ) ;
593+
594+ it ( 'should be reset when field changes on custom control' , async ( ) => {
595+ const { promise, resolve} = promiseWithResolvers < ValidationError [ ] > ( ) ;
596+
597+ @Component ( { selector : 'custom-control' , template : `` } )
598+ class CustomControl implements FormValueControl < string > {
599+ readonly value = model . required < string > ( ) ;
600+ readonly pending = input . required < boolean > ( ) ;
601+ }
602+
603+ @Component ( {
604+ imports : [ Field , CustomControl ] ,
605+ template : `<custom-control [field]="field()" />` ,
606+ } )
607+ class TestCmp {
608+ readonly f = form ( signal ( { x : '' , y : '' } ) , ( p ) => {
609+ validateAsync ( p . x , {
610+ params : ( ) => [ ] ,
611+ factory : ( params ) =>
612+ resource ( {
613+ params,
614+ loader : ( ) => promise ,
615+ } ) ,
616+ onSuccess : ( results ) => results ,
617+ onError : ( ) => null ,
618+ } ) ;
619+ } ) ;
620+ readonly field = signal ( this . f . x ) ;
621+ readonly customControl = viewChild . required ( CustomControl ) ;
622+ }
623+
624+ const fixture = act ( ( ) => TestBed . createComponent ( TestCmp ) ) ;
625+ const component = fixture . componentInstance ;
626+
627+ expect ( component . customControl ( ) . pending ( ) ) . toBe ( true ) ;
628+
629+ act ( ( ) => component . field . set ( component . f . y ) ) ;
630+ expect ( component . customControl ( ) . pending ( ) ) . toBe ( false ) ;
631+
632+ resolve ( [ ] ) ;
633+ await promise ;
634+ await fixture . whenStable ( ) ;
635+ expect ( component . customControl ( ) . pending ( ) ) . toBe ( false ) ;
636+ } ) ;
637+ } ) ;
638+
548639 describe ( 'readonly' , ( ) => {
549640 it ( 'should bind to native control' , ( ) => {
550641 @Component ( {
@@ -2184,49 +2275,6 @@ describe('field directive', () => {
21842275 } ) ;
21852276 } ) ;
21862277
2187- it ( 'should synchronize pending status' , async ( ) => {
2188- const { promise, resolve} = promiseWithResolvers < ValidationError [ ] > ( ) ;
2189-
2190- @Component ( {
2191- selector : 'my-input' ,
2192- template : '<input #i [value]="value()" (input)="value.set(i.value)" />' ,
2193- } )
2194- class CustomInput implements FormValueControl < string > {
2195- value = model ( '' ) ;
2196- pending = input ( false ) ;
2197- }
2198-
2199- @Component ( {
2200- template : ` <my-input [field]="f" /> ` ,
2201- imports : [ CustomInput , Field ] ,
2202- } )
2203- class PendingTestCmp {
2204- myInput = viewChild . required < CustomInput > ( CustomInput ) ;
2205- data = signal ( 'test' ) ;
2206- f = form ( this . data , ( p ) => {
2207- validateAsync ( p , {
2208- params : ( ) => [ ] ,
2209- factory : ( params ) =>
2210- resource ( {
2211- params,
2212- loader : ( ) => promise ,
2213- } ) ,
2214- onSuccess : ( results ) => results ,
2215- onError : ( ) => null ,
2216- } ) ;
2217- } ) ;
2218- }
2219-
2220- const fix = act ( ( ) => TestBed . createComponent ( PendingTestCmp ) ) ;
2221-
2222- expect ( fix . componentInstance . myInput ( ) . pending ( ) ) . toBe ( true ) ;
2223-
2224- resolve ( [ ] ) ;
2225- await promise ;
2226- await fix . whenStable ( ) ;
2227- expect ( fix . componentInstance . myInput ( ) . pending ( ) ) . toBe ( false ) ;
2228- } ) ;
2229-
22302278 it ( `should mark field as touched on native control 'blur' event` , ( ) => {
22312279 @Component ( {
22322280 imports : [ Field ] ,
0 commit comments