12

Is there a way to check whether control is required?

The problem arose when I implemented a dedicated form field component which accepts FormControl and has not only input but also validation errors. Since some field are required it's good to let the user know if it's required by *.

Is there a way to check the @Input() control: FormControl for Validators.required and display an asterisk?

Sergey
  • 5,085
  • 4
  • 29
  • 61

4 Answers4

16

You can do something like this:

import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms';

@Component({...})
export class AppComponent  {
  form: FormGroup = new FormGroup({
    control: new FormControl(null, Validators.required)
  });

  get validator() {
    const validator = this.form.get('control').validator({} as AbstractControl);
    console.log(validator);
    if (validator && validator.required) {
      return true;
    }
  }
}

And then in your Template:

<form [formGroup]="form" (submit)="onSubmit()">
  Control: <span *ngIf="validator">*</span> <input type="text" formControlName="control">
  <button>Submit</button>
</form>

NOTE: Just get the form control as a type of AbstractControl using this this.form.get('control').validator({} as AbstractControl);

This will return an Object with the list of validators that are present on your FormControl. You can then check for the required key in the Object. If it exists and its value is true then you can be sure that a Required Validator is applied on the FormControl.


Here's a Working Sample StackBlitz for your ref.

SiddAjmera
  • 32,111
  • 5
  • 45
  • 85
  • Thank you a lot. However, I needed to modify your code to `const validator = this.control.validator ? this.control.validator({} as AbstractControl) : '';` due to some fields with no validators – Sergey Nov 30 '18 at 13:05
  • What about validator `minLength` etc which has values? I've tried to console this but got only `{required: true}`. I'm investigating `console.log(validator)` – Sergey Nov 30 '18 at 13:09
  • That's not something that you can do out of the box. Read **[this thread](https://github.com/angular/angular/issues/13461)** for more. – SiddAjmera Nov 30 '18 at 14:07
  • I want to add that if you have a custom validator function in your validators it might throw errors since the object you are passing is strictly speaking no control. You can solve this by first checking if it the passed object has the setErrors function.`if (control.setErrors === undefined) return;` – StefanJanssen Oct 10 '19 at 08:18
14

I needed something slightly more abstract, so I've adapted the answer from @siddajmera a bit to be able to use on any field.

In your .ts file:

isRequiredField(field: string) {
    const form_field = this.testForm.get(field);
    if (!form_field.validator) {
        return false;
    }

    const validator = form_field.validator({} as AbstractControl);
    return (validator && validator.required);
}

Then, in your template file:

<div>
    <label>Some Field:<span *ngIf="isRequiredField('some_field')">*</span></label>
    <input [formControl]="form.controls['some_field']">
</div>
<div>
    <label>Some Field:<span *ngIf="isRequiredField('another_field')">*</span></label>
    <input [formControl]="form.controls['another_field']">
</div>
random_user_name
  • 23,924
  • 7
  • 69
  • 103
  • 1
    This answer is a bit better than the approved one, because it reduce possibility of `ERROR TypeError: form_field.validator is not a function`, just by setting the default value to getter method. – mpro Oct 01 '19 at 08:07
0

I'm a bit late to the game but I think this could be solved by using a Pipe. The answers above solve your problem but there is a small caveat. By directly using a method/getter in your template, this function is executed every change detection run as stated in this article. It might not be a performance issue in your short example but might be a problem in larger forms.

My solution

By using a pure Pipe the check on the provided control is fired once the input value to the Pipe has changed. I've added the ControlRequiredPipe to the AppModule providers and declarations parts. When the pipe is added to the providers part, it can be used in a Component TypeScript class as well. I've included this behavior in the OnSubmit function in the AppComponent.

Stackblitz example

AppComponent:

<form [formGroup]="form" (submit)="onSubmit()">
  Control: <strong *ngIf="form.get('control') | controlRequired">*</strong>
  <input type="text" formControlName="control">
  <button>Submit</button>
</form>
import { Component } from "@angular/core";
import { FormGroup, FormControl, Validators } from "@angular/forms";
import { ControlRequiredPipe } from "./control-required.pipe";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  form: FormGroup = new FormGroup({
    control: new FormControl(null, [
      Validators.required,
      Validators.minLength(8)
    ])
  });

  constructor(private controlRequiredPipe: ControlRequiredPipe) {}

  onSubmit() {
    alert(
      `The required state of the FormControl is: ${this.controlRequiredPipe.transform(
        this.form.get("control")
      )}`
    );
  }
}

ControlRequiredPipe:

import { Pipe, PipeTransform } from "@angular/core";
import { AbstractControl } from "@angular/forms";

@Pipe({
  name: "controlRequired"
})
export class ControlRequiredPipe implements PipeTransform {
  public transform(control: AbstractControl): boolean {
    //  Return when no control or a control without a validator is provided
    if (!control || !control.validator) {
      return false;
    }

    //  Return the required state of the validator
    const validator = control.validator({} as AbstractControl);
    return validator && validator.required;
  }
}

AppModule:

import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";

import { AppComponent } from "./app.component";
import { ControlRequiredPipe } from "./control-required.pipe";

@NgModule({
  imports: [BrowserModule, FormsModule, ReactiveFormsModule],
  declarations: [AppComponent, ControlRequiredPipe],
  providers: [ControlRequiredPipe],
  bootstrap: [AppComponent]
})
export class AppModule {}
Hkidd
  • 727
  • 7
  • 23
-5

You can use the required attribute.

<input type="text" formControlName="control" placeholder="Some Field" required>

Furthermore, it's helpful for CSS styling or accessibility purposes.

  • Deivid, welcome to StackOverflow. Sorry to begin your journey here with a down vote, but you didn't actually read the question, did you? This is not about making a field required, but checking *if it is*. – Robin May 12 '20 at 07:55