0
votes

I have an Angular 2 Reactive form using FormGroup. I need to compare the values of two fields, so I am passing a validator to the FormGroup upon creation. The validation works great:

this.form = this.formBuilder.group({
  add: this.formBuilder.group({
    mobile: ['', [
      Validators.required,
      this.duplicate
    ]],
    voice: [false]
  }),
  existing: this.formBuilder.array([])
}, {validator: this.validateDuplicates});

validateDuplicates(group: FormGroup): any {
  let adding = group.get('add').get('mobile').value;
  if (!adding) {
    return null;
  }

  let matches: boolean[] = (group.get('existing') as FormArray).controls
    .map(control => {
      return control.get('number').value;
    })
    .filter(val => val === adding);

  return matches.length > 0 ? {
    duplicate: {adding}
  } : null;
}

After I enter an invalid value, the form.errors.duplicate field is populated correctly, and the form group is marked with an ng-invalid CSS class in the DOM.

The only gap I have is that I would like a specific form control to be marked as ng-invalid when this group-level validator fails. Is there any way to do that? I have tried messing with the object returned from the validator function to match the nested layer of the target form control, to no avail.

In the short term I have been able to work around it by adding additional markup and styling on a specific element when the form is invalid, but ideally I would like to be able to get rid of that, and address specific error codes in the markup for that control only, rather than going up to the parent form group level:

<md-input-container class="primary" floatPlaceholder="never">
    <input mdInput name="mobile" type="text" pattern="^\([0-9]{3}\)\s[0-9]{3}\-[0-9]{4}$" placeholder="Number" formControlName="mobile" required>
    <md-hint *ngIf="!addControl.touched &amp;&amp; addControl.invalid" align="start" color="warn">(202) 555-5555</md-hint>
    <md-hint *ngIf="addControl.touched &amp;&amp; addControl.invalid" align="end" color="warn">Format must be (###) ###-####</md-hint>
    <!-- Workaround I'd like to remove -->
    <md-hint *ngIf="form.errors &amp;&amp; form.errors.duplicate">Numbers must be unique</md-hint>
</md-input-container>
1
At first, when you use reactive forms the recommended way (and maybe the right one) to add validators is in component, not in template.. so in your case, Pattern and required must go only in component. Also, there-s md-error in Material2 that you can use to show errors. Now about your problem, a plunker to demonstrate it would be good.developer033

1 Answers

1
votes

To mark a specific control as invalid and assign it an arbitrary error of your choice, you could use the setErrrors method on the control of your choice. This also sets the validity of the parent control/group.

For example after pinning down the faulty control[s] containing [a] duplicate value[s], you could do this in your custom validator method:

faultyCtrl.setErrors({duplicate: true});

Don't forget to also return the error from your custom validator to properly set the errors property on the group, because setErrors only sets the status property, but not the errors (see this for further info)