@@ -69,6 +69,189 @@ export interface Validator {
6969 registerOnValidatorChange ?( fn : ( ) => void ) : void ;
7070}
7171
72+ /**
73+ * A base class for Validator-based Directives. The class contains common logic shared across such
74+ * Directives.
75+ *
76+ * For internal use only, this class is not intended for use outside of the Forms package.
77+ */
78+ @Directive ( )
79+ abstract class AbstractValidatorDirective implements Validator {
80+ private _validator : ValidatorFn = Validators . nullValidator ;
81+ private _onChange ! : ( ) => void ;
82+
83+ /**
84+ * Name of an input that matches directive selector attribute (e.g. `minlength` for
85+ * `MinLengthDirective`). An input with a given name might contain configuration information (like
86+ * `minlength='10'`) or a flag that indicates whether validator should be enabled (like
87+ * `[required]='false'`).
88+ *
89+ * @internal
90+ */
91+ abstract inputName : string ;
92+
93+ /**
94+ * Creates an instance of a validator (specific to a directive that extends this base class).
95+ *
96+ * @internal
97+ */
98+ abstract createValidator ( input : unknown ) : ValidatorFn ;
99+
100+ /**
101+ * Performs the necessary input normalization based on a specific logic of a Directive.
102+ * For example, the function might be used to convert string-based representation of the
103+ * `minlength` input to an integer value that can later be used in the `Validators.minLength`
104+ * validator.
105+ *
106+ * @internal
107+ */
108+ abstract normalizeInput ( input : unknown ) : unknown ;
109+
110+ /**
111+ * Helper function invoked from child classes to process changes (from `ngOnChanges` hook).
112+ * @nodoc
113+ */
114+ handleChanges ( changes : SimpleChanges ) : void {
115+ if ( this . inputName in changes ) {
116+ const input = this . normalizeInput ( changes [ this . inputName ] . currentValue ) ;
117+ this . _validator = this . createValidator ( input ) ;
118+ if ( this . _onChange ) {
119+ this . _onChange ( ) ;
120+ }
121+ }
122+ }
123+
124+ /** @nodoc */
125+ validate ( control : AbstractControl ) : ValidationErrors | null {
126+ return this . _validator ( control ) ;
127+ }
128+
129+ /** @nodoc */
130+ registerOnValidatorChange ( fn : ( ) => void ) : void {
131+ this . _onChange = fn ;
132+ }
133+ }
134+
135+ /**
136+ * @description
137+ * Provider which adds `MaxValidator` to the `NG_VALIDATORS` multi-provider list.
138+ */
139+ export const MAX_VALIDATOR : StaticProvider = {
140+ provide : NG_VALIDATORS ,
141+ useExisting : forwardRef ( ( ) => MaxValidator ) ,
142+ multi : true
143+ } ;
144+
145+ /**
146+ * A directive which installs the {@link MaxValidator} for any `formControlName`,
147+ * `formControl`, or control with `ngModel` that also has a `max` attribute.
148+ *
149+ * @see [Form Validation](guide/form-validation)
150+ *
151+ * @usageNotes
152+ *
153+ * ### Adding a max validator
154+ *
155+ * The following example shows how to add a max validator to an input attached to an
156+ * ngModel binding.
157+ *
158+ * ```html
159+ * <input type="number" ngModel max="4">
160+ * ```
161+ *
162+ * @ngModule ReactiveFormsModule
163+ * @ngModule FormsModule
164+ * @publicApi
165+ */
166+ @Directive ( {
167+ selector :
168+ 'input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]' ,
169+ providers : [ MAX_VALIDATOR ] ,
170+ host : { '[attr.max]' : 'max ? max : null' }
171+ } )
172+ export class MaxValidator extends AbstractValidatorDirective implements OnChanges {
173+ /**
174+ * @description
175+ * Tracks changes to the max bound to this directive.
176+ */
177+ @Input ( ) max ! : string | number ;
178+ /** @internal */
179+ inputName = 'max' ;
180+ /** @internal */
181+ normalizeInput = ( input : string ) : number => parseInt ( input , 10 ) ;
182+ /** @internal */
183+ createValidator = ( max : number ) : ValidatorFn => Validators . max ( max ) ;
184+ /**
185+ * Declare `ngOnChanges` lifecycle hook at the main directive level (vs keeping it in base class)
186+ * to avoid differences in handling inheritance of lifecycle hooks between Ivy and ViewEngine in
187+ * AOT mode. This could be refactored once ViewEngine is removed.
188+ * @nodoc
189+ */
190+ ngOnChanges ( changes : SimpleChanges ) : void {
191+ this . handleChanges ( changes ) ;
192+ }
193+ }
194+
195+ /**
196+ * @description
197+ * Provider which adds `MinValidator` to the `NG_VALIDATORS` multi-provider list.
198+ */
199+ export const MIN_VALIDATOR : StaticProvider = {
200+ provide : NG_VALIDATORS ,
201+ useExisting : forwardRef ( ( ) => MinValidator ) ,
202+ multi : true
203+ } ;
204+
205+ /**
206+ * A directive which installs the {@link MinValidator} for any `formControlName`,
207+ * `formControl`, or control with `ngModel` that also has a `min` attribute.
208+ *
209+ * @see [Form Validation](guide/form-validation)
210+ *
211+ * @usageNotes
212+ *
213+ * ### Adding a min validator
214+ *
215+ * The following example shows how to add a min validator to an input attached to an
216+ * ngModel binding.
217+ *
218+ * ```html
219+ * <input type="number" ngModel min="4">
220+ * ```
221+ *
222+ * @ngModule ReactiveFormsModule
223+ * @ngModule FormsModule
224+ * @publicApi
225+ */
226+ @Directive ( {
227+ selector :
228+ 'input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]' ,
229+ providers : [ MIN_VALIDATOR ] ,
230+ host : { '[attr.min]' : 'min ? min : null' }
231+ } )
232+ export class MinValidator extends AbstractValidatorDirective implements OnChanges {
233+ /**
234+ * @description
235+ * Tracks changes to the min bound to this directive.
236+ */
237+ @Input ( ) min ! : string | number ;
238+ /** @internal */
239+ inputName = 'min' ;
240+ /** @internal */
241+ normalizeInput = ( input : string ) : number => parseInt ( input , 10 ) ;
242+ /** @internal */
243+ createValidator = ( min : number ) : ValidatorFn => Validators . min ( min ) ;
244+ /**
245+ * Declare `ngOnChanges` lifecycle hook at the main directive level (vs keeping it in base class)
246+ * to avoid differences in handling inheritance of lifecycle hooks between Ivy and ViewEngine in
247+ * AOT mode. This could be refactored once ViewEngine is removed.
248+ * @nodoc
249+ */
250+ ngOnChanges ( changes : SimpleChanges ) : void {
251+ this . handleChanges ( changes ) ;
252+ }
253+ }
254+
72255/**
73256 * @description
74257 * An interface implemented by classes that perform asynchronous validation.
0 commit comments