Skip to content

Allow passing validator classes to the FormControl constructor #24981

@mischkl

Description

@mischkl

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Performance issue
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
[ ] Other... Please describe:

Related issues: #12423

Current behavior

When writing custom validators it is sometimes useful to make use of injected dependencies, for instance in order to use server-side validation. As outlined in the article Custom Validators in Angular, the recommended way of accomplishing this is to define validators as classes that conform to the Validator interface. When using these validators with the FormControl API, however, the class cannot be passed as a validator but rather a function must be passed. This causes potential pitfalls for developers because they have to take special care to ensure that the validate function maintains the "this" context, either by binding it to the validator class or by defining it as an arrow function.

Expected behavior

It should be possible to pass validator classes to the FormControl constructor (and FormBuilder API) in addition to passing validator functions.

Minimal reproduction of the problem with instructions

As an example, take the following validator:

@Directive({
  selector: '[validateDomain][ngModel],[validateDomain][formControl],[validateDomain][formControlName]',
  providers: [
    { provide: NG_VALIDATORS, useExisting: forwardRef(() => DomainValidator), multi: true }
  ]
})
export class DomainValidator implements AsyncValidator {
  constructor(private validationService: ValidationService) {
  }

  validate(c: FormControl) {
    return this.validationService.validateDomain(c.value).pipe(map(res => res ? null : { domain: true }));
  }
}

The naive way to use it with a FormControl would be:

@Component({...})
class MyComponent {
  domainFormControl: FormControl;

  constructor(private domainValidator: DomainValidator) {
    this.domainFormControl = new FormControl('', [this.domainValidator.validate]);
  }
}

However, this would result in an error that validationService could not be found on undefined, since the "this" scope is lost when the domainValidator.validate function is passed to the FormControl constructor. The solution would be to either add a line this.validate = this.validate.bind(this) to the validator's constructor or to define validate as an arrow function: validate = (c: FormControl) => { ... }. Neither one of these options is obvious at first glance, and will probably need to be documented with comments in order to avoid being changed in the process of "code cleanup". In other words, this limitation of the FormControl API puts a lot of burden on the developer.

If the FormControl API were to support passing in class-based validators this pitfall would be unlikely to crop up in new code, and the framework would be more consistent in its treatment of function-based validators and class-based validators as interchangeable.

What is the motivation / use case for changing the behavior?

See above.

Environment


Angular version: 6.0.9


Browser:
- [x] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions