DEV Community

Cover image for Angular form field types
Ayyash
Ayyash

Posted on • Originally published at garage.sekrab.com

Angular form field types

Today we are going to dig into validating all field types. We begin with the simpler ones: the string-based ones:

  • Email, tel, and text, are the easy ones; nothing that needs extra care
  • Textarea: This is like text input. It may need extra styling.
  • URL: This is not handled by Angular, so we can use a pattern matcher.
  • Number: The browser number field is powerful enough to prevent non-numeric values, but we can still use a pattern matcher for more control.

Other types are:

  • Select box: The only generic validation needed is required
  • Checkboxes and radio buttons: The most obvious validation is "at least one is selected"
  • Hidden fields: These are like text fields but need styling.

Textarea

This one is straightforward, the common validation is minimum and maximum number of characters, which is done via the native minlength and maxlength . This has already been covered. Looks like the style is also still holding.

Textarea validation

Select box

The pain—or not! Using the first test: a list with “required” validation, the only missing thing we can style is the arrow. It begins with appearance: none. But that's a project-related choice.

UX tip: if it is required, select one by default.

Some high-security websites (like banking) mandate that the user makes a deliberate choice—whatever that means—but those were the exact words I was once told. In this case, an option with empty value acts as a null value and will trigger an invalid flag.

<cr-input placeholder="Operating system">
  <select crinput id="os" formControlName="os" [required]="true">
    <option value="">Select</option>
    <option value="1">Windows</option>
    <option value="2">Mac</option>
    <option value="3">Linux</option>
    <option value="4">Android</option>
  </select>
</cr-input>
Enter fullscreen mode Exit fullscreen mode

This is how it looks when invalid

Selectbox validation
This actually worked better than I hoped for! Let’s move on.

Checkbox

For a single checkbox with required, functionally it works because it will be valid if true. But it looks awful. The label needs to be after the checkbox. Sucks doesn’t it?

// form component, the preferred syntax
<cr-input placeholder="Terms and conditions" error="Please accept">
  <input type="checkbox" name="accept" [required]="true" crinput id="accept" formControlName="accept">
</cr-input>
// ...
this.fg = this.fb.group({
    //...
    accept: [],
});

Enter fullscreen mode Exit fullscreen mode

Won’t show you how it looks, we’ll fix all styles later. But it does work as expected.

Multiple checkboxes

There are a lot of ways to create a group of checkboxes with the same name. This article is not about that, but the end result I want to reach is a validation message that falls into place with other fields.

The four ways I know of are:

  • formGroupName with checkboxes
  • formArrayName with checkboxes
  • Checkboxes that update a validation state of the form
  • Checkboxes that update a hidden input that is part of the form

Here is a example of formGroupName that uses a cross-form validation function to set the state of the form as invalid. We make use of the invalidForm property to display the error.

// form componennt
template: `
  <cr-input placeholder="Colors" error="At least one color" [invalidForm]="fg.get('colors').hasError('atleastOne')">
    <div formGroupName="colors">
      <label>
        <input type="checkbox" name="colors" id="color1" formControlName="red">
        Red
      </label> <br>
      <label>
        <input type="checkbox" name="colors" id="color2" formControlName="black">
        Black
      </label> <br>
      <label>
        <input type="checkbox" name="colors" id="color3" formControlName="green">
        Green
      </label> <br>
    </div>
    <ng-container helptext>Choose at least one </ng-container>
  </cr-input>`

// ...
this.fg = this.fb.group({
  colors: this.fb.group({
    red: [],
    black: [],
    green: [],
  }, { validators: this.atleastOne})

});

atleastOne = (control: AbstractControl): ValidationErrors | null => {
  // if all controls are false, return error
  const values = Object.values(control.value);
  if (values.some(v => v === true)) {
    return null;
  }
  return { atleastOne: true };

};
Enter fullscreen mode Exit fullscreen mode

The error shows when:
fg.get('colors').hasError('atleastOne')

It works, but it looks like the following. That, we’ll fix later.

Checkboxes

We can also assign the directive to the formGroupName field, like this:

<cr-input placeholder="Colors" error="At least one color">
  // simply add crinput directive here
  <div formGroupName="colors" crinput>
    ...
  </div>
  <ng-container helptext>Choose at least one </ng-container>
</cr-input>
Enter fullscreen mode Exit fullscreen mode

Keeping the validator as is, no need to use invalidForm. This validates nicely, and it looks as follows. A bit better.

Checkbox group

Another way is to have the checkboxes separated in their own HTML element. This works as well if we want to display the bubble in a different location.

<cr-input placeholder="Colors" error="At least one color" [invalidForm]="fg.get('colors').hasError('atleastOne')">
    Select a color 
</cr-input>

<div formGroupName="colors">
    <label>
      <input type="checkbox" name="colors" id="color1" formControlName="red">
      Red
    </label> <br>
    <label>
      <input type="checkbox" name="colors" id="color2" formControlName="black">
      Black
    </label> <br>
    <label>
      <input type="checkbox" name="colors" id="color3" formControlName="green">
      Green
    </label> <br>
</div>
Enter fullscreen mode Exit fullscreen mode

And it looks like this:

Checkboxes validation

This is all possible because we kept it dead simple with the form feedback error class. We just need a bit more touch-ups, every project may have its own styling.

Radio button

As for radio buttons, they are bound to a single formControlName. The thing about radio buttons, is that the validation is almost always a cross-form validation. Best experience dictates that at least one should be checked by default. If none was checked by default, notice how by now we can use crinput directive on a normal HTML element, and get away with it. We use the invalidForm property to react to the invalid radio button.

<!-- add invalidForm to component -->
<cr-input placeholder="Gender" [invalidForm]="fg.get('gender').invalid">
  <!-- use crinpiut to div?! -->
  <div crinput>
    <input type="radio" name="gender" value="male" [required]="true"  id="male" formControlName="gender"> Male
    <input type="radio" name="gender" value="female" [required]="true" id="female" formControlName="gender"> Female
  </div>
</cr-input>
Enter fullscreen mode Exit fullscreen mode

Looks as follows:

Radiob buttons

Let’s move on.

Hidden field

Hidden inputs simplify validation that would otherwise be challenging. They are mostly treated like text fields, with special styling. We will take care of that later.

Validation common functions

We created a patterns collection to reuse throughout our project, but we can also create common validation functions for reuse. Let's go through some common examples to build it up. Next episode, inshalla. 😴

Did you know that UNRWA is complicit in genocide?

Resources

Angular form field types - Sekrab Garage

Taming Angular forms. Today we are going to dig into validating all field types. We begin with the simpler ones: the string-based ones:<ul><li>Email, tel, and text, are the easy ones; nothing that needs extra care</li><li>.... Posted in Angular, Design

favicon garage.sekrab.com

Top comments (0)