Skip to content

Ability to set a custom message while working with Validators #46405

@kasir-barati

Description

@kasir-barati

Which @angular/* package(s) are relevant/related to the feature request?

forms

Description

It is really intimating and obvious that in most cases we need to show custom messages to the user but IDK why we cannot specify the error message in the component's class and we have to deal with it in a painful way in templates. Another thing is that this stackoverflow Q&A shows is that I am not alone 😄 .

Proposed solution

IMO it would be more readily to specify error message in the component's class and then use a common component to show error messages as it is described here

So in login.component.ts we have:

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css'],
})
export class AComponent {
  public loginForm: FormGroup;
  public passwordSpecialChar = '!@#$&*';
  public passwordShouldContainsSpecialChar = /(?=.*[!@#$&*])/;
  public passwordShouldContainsOneLowercaseLetter = /(?=.*[a-z])/;
  public passwordShouldContainsOneCapitalLetter = /(?=.*[A-Z])/;
  public passwordShouldContainsOneDigit = /(?=.*[0-9])/;
  constructor(private formBuilder: FormBuilder) {}

  ngOnInit(): void {
    this.loginForm = this.formBuilder.group({
      username: [
        '',
        Validators.compose([
          Validators.required({ customeMessage: 'Please enter username' }),
          Validators.minLength(6, { customeMessage: 'Username should be at least 6 character' }),
          Validators.maxLength(320, { customeMessage: 'Username cannot be more that 320 character' }),
          Validators.pattern(/[\w_-\d]/, { customeMessage: 'Username can only contains alphanumeric, hyphens, and dashes' }),
        ]),
      ],
      password: [
        '',
        Validators.compose([
          Validators.minLength(8, { customeMessage: 'c message 1' }),
          Validators.required({ customeMessage: 'c message 2' }),
          Validators.pattern(this.passwordShouldContainsSpecialChar, { customeMessage: 'c message 3' }),
          Validators.pattern(
            this.passwordShouldContainsOneLowercaseLetter,
            { customeMessage: 'c message 4' }
          ),
          Validators.pattern(
            this.passwordShouldContainsOneCapitalLetter,
            { customeMessage: 'c message 5' }
          ),
          Validators.pattern(this.passwordShouldContainsOneDigit, { customeMessage: 'c message 6' }),
        ]),
      ],
    });
  }
}

and in the login.component.html we have:

<div>
  <form [formGroup]="loginForm" (ngSubmit)="login(loginForm.value)">
    <div>
      <div>
        <label for="username">username</label>
        <span
          class="text-danger"
          *ngIf="loginForm.get('username').hasError('required')"
        >
          *
        </span>
      </div>
      <div>
        <!-- formControlName Syncs FormControl in an existing FormGroup instance to a form control element by name. -->
        <input
          placeholder="Enter your username"
          type="text"
          name="username"
          id="username"
          formControlName="username"
        />
      </div>
      <div
        *ngIf="
          loginForm.get('username').invalid &&
          loginForm.get('username').errors &&
          (loginForm.get('username').dirty ||
            loginForm.get('username').touched)
        "
      >
        <app-validation-errors
          [errors]="loginForm.get('username').errors"
        ></app-validation-errors>
      </div>
    </div>
    <div>
      <div>
        <label for="password">password</label>
        <span
          class="text-danger"
          *ngIf="loginForm.get('password').hasError('required')"
        >
          *
        </span>
      </div>
      <div>
        <input
          placeholder="Enter your Password"
          type="password"
          formControlName="password"
          name="password"
          id="password"
        />
      </div>
      <div
        *ngIf="
          loginForm.get('password').invalid &&
          (loginForm.get('password').touched ||
            loginForm.get('password').dirty)
        "
      >
        <app-validation-errors
          [errors]="loginForm.get('password').errors"
        ></app-validation-errors>
      </div>
    </div>
    <div>
      <div>
        <button
          [dissabled]="!loginForm.valid"
          type="submit"
          value="Login"
        ></button>
      </div>
      <div>Form Status: {{ loginForm.status }}</div>
    </div>
  </form>
</div>

and in the validation-errors.component.html I could state error messages vividly

<ng-container
  class="text-danger"
  *ngIf="errors && errors['required']"
>
  {{
    errors['required'].customMessage ? errors['required'].customMessage : 'This field is required'
  }}
</ng-container>

<!-- The same thing for the rest of it -->

Why am I motivated and think this is a unquestionable need?

I used to be a backend developer and I really appreciate class-validator. Now I came to learn Angular but I really feel that this is a dire need that has been forgotten or maybe it does not felt at all. BTW this is just an opinion.

Alternatives considered

Or at least we should have the power to change the default error message.
In the login.component.ts:

/* ... */
Validators.required({ message: 'Please enter username' }),
/* ... */

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions