Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reactive form validator can't work with not static input #23293

Closed
brachi-wernick opened this issue Apr 10, 2018 · 9 comments
Closed

Reactive form validator can't work with not static input #23293

brachi-wernick opened this issue Apr 10, 2018 · 9 comments

Comments

@brachi-wernick
Copy link

brachi-wernick commented Apr 10, 2018

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ X ] Bug report  
[ ] 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

Current behavior

Only in reactive forms.

When I use angular built in validators or write a custom validator that is based on input, (like maxLength is based on the max length value) , when this input changed it doesn't affect the validation status of the control.

can be split into 2 issues:

  1. ValidatorFn can't get input that can be changed
    when creating a control and set the validation function, function type must be ValidatorFn :
export interface ValidatorFn {
    (c: AbstractControl): ValidationErrors | null;
}

so, my custom input must be declared inside the closure, and can't be changed:

like it is in maxLength:

maxLength(maxLength:number): ValidatorFn {
  
    return (control: AbstractControl): ValidationErrors | null => {
     
      const length: number = control.value ? control.value.length : 0;
      return length > maxLength?
          {'maxlength': {'requiredLength': maxLength, 'actualLength': length}} :
          null;
    };
}

** this can work only with mutable object, for example:

maxLength(maxLength:{length:number}): ValidatorFn {
  
    return (control: AbstractControl): ValidationErrors | null => {
     
      const length: number = control.value ? control.value.length : 0;
      return length > maxLength.length ?
          {'maxlength': {'requiredLength': maxLength, 'actualLength': length}} :
          null;
    };
}
  1. validation invoked only when control value is changed not if the input is changed.
    (This also work with model driven approach, because I have the api registerOnValidatorChange and I can call it when validation directive input is changed.)
    the way to hack it is to call control.updateValueandValidity() when input is changed.

Expected behavior

I want be able to change my validator input. and affect the validation result (this is supported when it is model driven form, because it's not implemented as closure, but as directive with inputs)

Minimal reproduction of the problem with instructions

In this example I have length initialized to 0, and after ngOnInit I change it to another number, this number isn't affect the validator, and it keep using the origin value
https://stackblitz.com/edit/angular-spqbfs?file=app%2Fapp.component.ts

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

input to the validator can be changed after the first initialization, or by back-end service or by user input

@brachi-wernick brachi-wernick changed the title Reactive form ValidatorFn can't work with not static input Reactive form validator can't work with not static input Apr 10, 2018
@ngbot ngbot bot added this to the needsTriage milestone Apr 10, 2018
@RKaczmarek
Copy link

hi @brachi-wernick,

I don't think angular needs to be changed here. It behaves like expected. The maxlength validator takes a static value for the length and that's not wrong.

It sound like you just need to implement the custom validator with the maxlength outside the custom validation creator function to work.

@Component({})
export class ValidationExampleComponent implements OnInit {
    maxLength: number = 0;
    formGroup: FormGroup;

    constructor(
        private fb: FormBuilder,
    ) {
        this.createForm();
    }

    ngOnInit() {
        this.maxLength = 5; // change whenever you want
    }

    createForm(): void {
        this.formGroup = this.fb.group({
            anyInput: [
                '',
                this.variableMaxLengthValidator()
            ]
        });
    }

    variableMaxLengthValidator(): ValidatorFn {
        return (c: AbstractControl) => {
            const length = (c.value as string).length;
            return length > this.maxLength
                ? {'maxlength': {'requiredLength': this.maxLength, 'actualLength': length}}
                : null;
        }
    }
}

@brachi-wernick
Copy link
Author

brachi-wernick commented Apr 17, 2018

@RKaczmarek what you suggested won't work if I want my validator to be in a separate class outside the component. To be reused in other components. Also my issue isn't just with the maxLength validator. But with the validator interface that can't get a more input rather then the control. Problem is with any reusable custom validator that need more inputs.

@trotyl
Copy link
Contributor

trotyl commented Apr 17, 2018

@brachi-wernick ValidatorFn is only a function, whether is pure or stateful is totally decided by yourself.

@RKaczmarek
Copy link

@brachi-wernick it's your decision where to put the logic. outside the component class or inside doesn't matter. Here's a simple example:

export class MyVariableValidator {
    maxLength: number = 0;
    
    get validator(): ValidatorFn {
        return (c: AbstractControl) => {
            const length = (c.value as string).length;
            return length > this.maxLength
                ? {'maxlength': {'requiredLength': this.maxLength, 'actualLength': length}}
                : null;
        }
    }
}

@Component({})
export class ValidationExampleComponent implements OnInit {
    
    variableValidator: MyVariableValidator;
    formGroup: FormGroup;

    constructor(
        private fb: FormBuilder,
    ) {
        this.variableValidator = new MyVariableValidator();
        this.createForm();
    }

    ngOnInit() {
        this.variableValidator.maxLength = 5; // change whenever you want
    }

    createForm(): void {
        this.formGroup = this.fb.group({
            anyInput: [
                '',
                this.variableValidator.validator
            ]
        });
    }

}

@brachi-wernick
Copy link
Author

@RKaczmarek thank you on your example. It still seems like a workaround, I expect from framework to give better solution then that.
Also it doesn't help with the second problem I wrote. That validation isn't invoked when I change the input for the validation. And not touch the form control itself. And this is supported in template driven form.

@kara
Copy link
Contributor

kara commented May 1, 2018

This works as intended. As others have mentioned, there are code patterns that can ensure your other validator args are applied in the way you want.

@kara kara closed this as completed May 1, 2018
@andreElrico
Copy link

Kara if you close this issue because "code patterns" exist please point to that "code patterns" that dynamically and scalably retrigger validation when 1 of possibly MANY depending fields change.

I believe you did not fully understand the OPs request.

@6TELOIV
Copy link

6TELOIV commented Jul 17, 2019

@brachi-wernick could you make a setter and getter for the param you want to make the validator update, and then call the change function there, registered with registerOnValidatorChange? That should get the desired behavior

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 15, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants