Paul Evans
Paul Evans

Reputation: 394

Run control Validators on custom value or how not to run them at all

Long stroy short, i'm currently working on a directive that applies a function to a control value when modified. That function modifies the control value only in on the model side. That goes like this :

// import ...

// @Directive
export class ControlValuePipeDirective implements OnInit{

  @Input() ngxControlValuePipe?: (value? :any) => any;

  constructor(
    private ngControl: NgControl
  ) {}

  ngOnInit(): void {
    if (this.ngxControlValuePipe === undefined) {
      console.info("No method was provided to the ngxControlValuePipe directive. Ignoring it.")
    }else{
      this.ngControl.valueAccessor?.registerOnChange((value: any) => {
          this.ngControl.control?.patchValue(this.ngxControlValuePipe ?
            this.ngxControlValuePipe(value) : value,
            {emitModelToViewChange: false})
        }
      )
    }
  }
}

The problem I have is when I add validators to a control like so :

// import ...

// @Component
export class AppComponent {
  public profileForm: FormGroup;

  constructor() {
    const streetControl = new FormControl('', [Validators.maxLength(10)]);
    // this.profileForm = ...
  }

  public editValueOnTheFly(value: any): any{
    return value + ' this more than ten chars length string';
  };
}
<!-- more html -->
<input id="street" type="text" formControlName="street" [ngxControlValuePipe]="editValueOnTheFly">
<!-- more html -->

If my streetControl control value gets modified in the view, then the models get's edited using the editValueOnTheFly method, which adds a long string to the model value. This triggers the validation and invalidates the control as the new model value is longer than 10 characters.

Is there any way I can run the validators on the value that is shown in the view (which is not modified)?

Upvotes: 0

Views: 113

Answers (1)

John Malkoln
John Malkoln

Reputation: 461

Because you want to avoid validation of the value modified by ControlValuePipeDirective, I propose you to separate your data model and form model.

ControlValuePipeDirective should not update form's model:

export class ControlValuePipeDirective implements OnInit{
    ...
    ngOnInit(): void {
        if (!this.ngxControlValuePipe) { return; }

        this.ngControl.valueAccessor?.registerOnChange((value: any) => {
            this.ngxControlValuePipe(value); 
        }
    }
}

AppComponent's editValueOnTheFly will be responsible for profile model update:

interface ProfileModel {
    street: string;
    ...
}

export class AppComponent {
    public profileForm: FormGroup;
    private profileModel: ProfileModel;
    ...
    public editValueOnTheFly(value: any): any{
        const updatedValue = value + ' this more than ten chars length string';
        this.profileModel = { ...this.profileModel, street: updatedValue };
    }
}

Also profileModel field can be replaced by Subject, to be able to subscribe to the profile model's changes.

Upvotes: 0

Related Questions