0

In the parent component I define the form.

  cartForm = new FormGroup({
    company: new FormControl('', [Validators.required]),
    costCenter: new FormGroup({
      account: new FormControl('', [Validators.required]),
      activity: new FormControl('', [Validators.required, Validators.maxLength(5), Validators.minLength(5)]),
      project: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(10)]),
    })
  });

then the formControl project is used in a child component. In this component I need a custom validation and I can achive that by just adding

  validate(control: AbstractControl): ValidationErrors | null {
    // To my validation here
  }

And that works except that the Validators specified in the parent componet are overwritten.

Second approach would be to create a custom validator class.. but then I can't get the @Input from the component..?

Update: This is the component

@Component({
  selector: 'ssp-project',
  templateUrl: './project.component.html',
  styleUrls: ['./project.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProjectComponent implements ControlValueAccessor, OnInit {

  @Input('companyNumber') set companyNumber(companyNumber: string) {
    this._companyNumber = companyNumber;
  }
  private _companyNumber: string;

  constructor(
    @Optional() @Self() public ngControl: NgControl,
  ) {
    if (this.ngControl != null) {
      // Setting the value accessor directly (instead of using the providers) to avoid running into a circular import.
      this.ngControl.valueAccessor = this;
    }
  }

  onTouched = (_value?: any) => { };
  onChanged = (_value?: any) => { };

  writeValue(val: string): void {
    if (val) {
      this.ngControl.control?.setValue(val);
    }

  }

  registerOnChange(fn: any): void {
    this.onChanged = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  registerOnValidatorChange(fn: any): void {
    this.onChanged = fn;
  }

  ngOnInit() {
    // this.ngControl.control.setValidators([this.validate.bind(this)]);
    this.ngControl.control.setValidators(this.ngControl.control.validator ? [this.ngControl.control.validator, this.validate] : this.validate);
    this.ngControl.control.updateValueAndValidity();
  }

  validate(control: AbstractControl): ValidationErrors | null {

    if (this._companyNumber && control.value) {
      return this._costCenterService
        .validateProject(control.value, this._companyNumber)
        .pipe(
          debounceTime(3000),
          map(projectIsValid => {
            return { invalid: !projectIsValid };
          })
        ).subscribe();

    }
  }
}

1 Answer 1

1

You can add a validator to the list of existing validators using the validator property of the FormControl

control.setValidators(control.validator ? [ control.validator, this.validate ] : this.validate);

Edit: since the code was added in the question, the focus has shifted to async validators. E.g.:

control.setAsyncValidators(
  control.asyncValidator
    ? [control.asyncValidator, this.asyncValidator]
    : this.asyncValidator
);

// definition of asyncValidator
asyncValidator(ctrl: AbstractControl): Observable<ValidationErrors | null> {
  // Whatever your validation does asynchronously
  return of(null);
}
Sign up to request clarification or add additional context in comments.

6 Comments

That looks like it might work.. but when I test it the @Input data is gone.. :(
I updated my question to also contain the component code
Solved it.. was just doing it the wrong way :) thx!
Follow up question.. validate() returns a Observable.. but how do I subscribe to it? ```` validate(control: AbstractControl): Observable<ValidationErrors | null> { if (this._companyNumber && control.value) { return this._costCenterService .validateProject(control.value, this._companyNumber) .pipe( map(projectIsValid => { if (projectIsValid) { return null; } else { return { validProject: projectIsValid }; } }) ); } } ````
Well, seeing your code now, you actually need to use setAsyncValidators instead, and return the observable directly (not the subscription, so don't call subscribe() yourself). Check out the official documentation regarding async validators.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.