How to add 'required field' asterisk to angular reactive form inputs
Asked Answered
T

10

19

I am developing one application with reactive dynamic angular form. This forms fields are coming from an API request and is generated dynamically.

I really need to add 'required field' asterisk (*) to my form inputs which are required. How to achieve this ?

The below is how my form fields look like.

<ng-container *ngIf="input1.type=='string'" [hidden]="False">
 <div>
  <mat-form-field>
   <input matInput   [formControlName]="input1.field_name"  type="text"  placeholder="{{input1.title}}">
  </mat-form-field>   
 </div>
</ng-container>
Tanney answered 23/4, 2018 at 7:6 Comment(12)
@ochs.tobi that's not what he asked.Clamp
No he give me right suggestion, Thats what I wanted to achieve.Tanney
Are all fields required, or only some? Can you show us the rendered HTML for a required field?Toggery
@Jaydeep didn't you want to add an asterisk to your required fields ?Clamp
Take a look to angular.io/guide/form-validation#reactive-form-validationPaulson
@trichetriche Yes, and adding required tags achieve the same. It has added the asterisk to my required field. Can you tell me what did you perceive from my question?Tanney
@Jaydeep I didn't even know that adding the required attribute to the inputs would add the asterisk. My bad on this one !Clamp
@trichetriche : same here. I was searching it for a long time in material docs. and it was there in angular docs itself.Tanney
@trichetriche but who had replied? Your comment has made he/she delete his comment ???Tanney
If he/she is not going to add it as answer, I will add up myself, giving all credits to the one who replied:-)Tanney
You should do that, this way no-one has reputation for stealing the answer of someone else !Clamp
this way no-one has reputation for stealing the answer of someone else ! – I really dont get this!!! But I think I should do that :PTanney
T
5

My solution by generate new Directive:

  ng generate directive directive/mark-asterisk

This is full code of directive:

import {Directive, ElementRef, Input, OnInit} from '@angular/core';
import {FormGroup} from '@angular/forms';

@Directive({
  selector: '[appMarkAsterisk]'
})
export class MarkAsteriskDirective implements OnInit {
  @Input() formGroup: FormGroup;
  @Input() controlName: string;

  constructor(private elementRef: ElementRef) {
  }

  ngOnInit(): void {
    const isRequired = this.formGroup.controls[this.controlName]?.errors?.required;
    if (isRequired) {
      this.elementRef.nativeElement.innerHTML = '*';
    }else{
      this.elementRef.nativeElement.innerHTML = '';
    }
  }
}

Now in your HTML form, use it like this:

<label>Company name: <span appMarkAsterisk [formGroup]="formGroup" [controlName]="'companyName'"></span></label>
Theurer answered 25/5, 2021 at 2:45 Comment(1)
Can't bind to 'controlName' since it isn't a known property of 'span' i was thinking the input on the directive would silence thisProtero
B
3

Should be fixed when Angular v13 is released. https://github.com/angular/components/pull/23362

Babb answered 13/9, 2021 at 17:12 Comment(0)
P
2

Html

<span *ngIf = "input1?.required">*</span>

.ts

You want to require a input depending on some conditions. Have a look here

Pillsbury answered 23/4, 2018 at 7:36 Comment(0)
M
1

It seems like the actual validators will run from the typescript file but in order to actually get the asterisk you can just edit the html file.

<input matInput   [formControlName]="input1.field_name"  type="text"  [placeholder]="{{input1.title}}" required>
Manville answered 21/5, 2018 at 18:19 Comment(2)
Angular does not encourage using attribute values with reactive forms.Bushed
@AdamPlocher It should have square brackets if the value is not a string literalCecilacecile
C
1

Bind the required attribute to the input to display * on required validation from the reactive form field.

<input formControlName="name" [required]="formGroup.get('name').errors !== null && formGroup.get('name').errors.required">
Circassia answered 29/4, 2020 at 6:44 Comment(1)
This is template driven, OP asked for reactive forms.Tanika
D
1

Making a directive or using required attribute on the element can be a good temporary solution, but this should be working and will be fixed soon !

Material issue here: https://github.com/angular/components/issues/2574?_pjax=%23js-repo-pjax-container.

A lot of temporary solutions are discussed in the comments. I'll try to update this answer when the bug fix is released.

Darrelldarrelle answered 27/8, 2021 at 13:6 Comment(0)
L
1

While we don't have a definitive solution, here is my suggestion:

@Component({
  selector: "app-root",
  template: `
    <form [formGroup]="formGroup">
      <mat-form-field>
        <input
          matInput
          type="text"
          [required]="isRequired(input1.field_name)"
          [placeholder]="input1.title"
          [formControlName]="input1.field_name"
        />
      </mat-form-field>
    </form>
  `,
})
export class AppComponent {
  readonly formGroup = new FormGroup({
    name: new FormControl("", Validators.required),
  });

  readonly input1 = { field_name: "name", title: "Name", type: "string" };

  isRequired(name: string): boolean {
    return this.formGroup.get(name)?.hasValidator(Validators.required) ?? false;
  }
}
Legatee answered 14/9, 2021 at 1:50 Comment(0)
B
1

First create a directive to add the asterisk without messing with input attributes:

import { AfterContentChecked, Directive, Optional } from "@angular/core";
import { AbstractControl } from "@angular/forms";
import { MatFormField, MatInput, MatSelect } from "@angular/material";

/**
 * Input/Select into FormField consider Validator.required from reactive form if the [required] attribute is missing in the template
 */
@Directive({
  selector: 'mat-form-field:has(input:not([required])), mat-form-field:has(mat-select:not([required]))'
})
export class ReactiveAsteriskDirective implements AfterContentChecked {
  constructor(@Optional() private matFormField: MatFormField) { }

  ngAfterContentChecked() {
    if (this.matFormField) {
      const ctrl = this.matFormField._control;
      if (ctrl instanceof MatInput || ctrl instanceof MatSelect)
        if (ctrl.ngControl)
          if (ctrl.ngControl.control)
            if (ctrl.ngControl.control.validator)
              if (ctrl.ngControl.control.validator({} as AbstractControl))
                ctrl.required = ctrl.ngControl.control.validator({} as AbstractControl).required;
    }
  }
}

Now you might want to make the asterisk appear with red color, since it indicates a required input. To do so, add this to your component CSS:

:host ::ng-deep .mat-placeholder-required {
  color: red;
}
Bootjack answered 11/12, 2021 at 17:50 Comment(0)
H
1

We can implement an attribute directive that adds a required attribute to your element when the corresponding form control has the Validators.required validator:

import { Directive, ElementRef, OnInit, ViewContainerRef } from '@angular/core';
import { FormControlName, NgControl, Validators } from '@angular/forms';

@Directive({
  selector: '[formControl], [formControlName]',
  standalone: true,
})
export class FormControlRequiredAttributeDirective implements OnInit {
  constructor(private elementRef: ElementRef, private ngControl: NgControl) {}

  ngOnInit(): void {
    if (
      (this.ngControl instanceof FormControlName || this.ngControl instanceof FormControlDirective) &&
      this.ngControl.control.hasValidator(Validators.required)
    ) {
      this.elementRef.nativeElement.required = 'true';
    }
  }
}

When you import the directive into your component, it will work:

@Component({
  imports: [ReactiveFormsModule, FormControlRequiredAttributeDirective],
  // ...
})
class LoginFormComponent {
  formGroup: FormGroup = new FormGroup({
    email: new FormControl('', Validators.required),
  });
}
<form [formGroup]="formGroup">
  <label>
    <span>Email</span>
    <input type="email" formControlName="email" />
  </label>
</form>

This way, the validators serve as the single source of truth, eliminating the need for repetition.

Hoodoo answered 1/6, 2024 at 2:37 Comment(0)
C
-1

I was setting the required bit in the FormGroup, so I shouldn't need to set it in the html too. The css class ng-star-inserted was added to the class list, but the star wasn't added. I just added what I think should have been the css:

mat-label.ng-star-inserted::after {
    content: " *";
}

EDIT: I looked at what angular does with template forms with the [required]="condition", and it looks like it adds a <span class="ng-star-inserted"> *</span> after the label.

I don't know why adding the css worked for me. Now that I did this test, all the other elements are marked with .ng-star-inserted. all of them inputs/labels/divs/dialogs/icons. So when I put this back in, I get stars everywhere.

Cowitch answered 3/12, 2020 at 22:1 Comment(1)
This is confusing.Weathercock

© 2022 - 2025 — McMap. All rights reserved.