Attribute Directives ❤ Angular Forms

Tomasz Kula
Netanel Basal
Published in
3 min readJul 30, 2018

--

In this article, we’ll examine creating Angular Directives that interact with the Angular Forms controls.

We’ll do that by creating an attribute directive which hooks into the underlying form control and changes the directive’s host element border.

The directive will support both, template driven and reactive forms.

Directive’s API

We start by creating the directive and defining its API. The @Input names are prefixed with the directive’s selector. We rename them to keep the variables names short and easier to read, and we set some sensible defaults.

The colors dictionary has the possible form validation states as keys, and will be used to style the border.

NgControl

To create a truly reusable directive we should support both, template and reactive forms. But what exactly do we Inject? The user can use any of the following:

  • NgModel (Template Driven Forms)
<input [(ngModel)]="value" appValidationBorder>
  • FormControlName (Reactive Forms)
<input formControlName="value" appValidationBorder>
  • FormControlDirective (Reactive Forms)
<Input [formControl]="value" appValidationBorder>

Luckily for us, they all are all provided as the NgControl.

That means that we can inject the NgControl, and the Angular DI framework will provide us the closest form control directive. We also make sure to limit the injection with the @Self decorator.

constructor(@Self() ngControl: NgControl) {}

By using NgControl we get access to properties like control value, validation status, errors and more.

Most of the properties fall through to the AbstractControl class you are probably familiar with if you have worked with Angular Forms before.

Host Binding

Now that we have access to the form control, let’s use it to show or hide the validation border using the @HostBinding decorator.

We’ll display the validation border only after the user has interacted with the form control.

To do that, we will inspect the NgControl touched and dirty properties.

The checks for dirty and touched will prevent the border from showing until the user:

  • changes the value, turning the control dirty,
  • blurs the form control element, setting the control to touched.

One Time Configuration

Let’s finish up with providing a nice way to configure the directive. It’s important to keep the UI consistent, so it would be nice to set the directive configuration once, and have it applied in the entire app.

First, let’s create the config interface

Next we’ll implement the static forRoot method on the ValidationBorderModule class. It will provide the validation config under the VALIDATION_BORDER_CONFIG token.

Lastly, let’s consume the config inside the directive with the use of the @Inject decorator.

With forRoot method implemented, we have a clean way to configure the directive. We can import it once in the app.module, and it will configure every directive used in the entire app.

Template Driven Example

Reactive Example

Summary

We’ve succeeded in creating flexible attribute directive that works well with both template and reactive angular forms.

In the process we have learned to use NgControl to inject the closest form directive and how to use @HostBinding to reflect the form status by styling the host element’s border.

We finished up by providing a convenient way to configure the directive with the forRoot method.

If you like the content, please clap 👏👏👏

To read more about Angular, follow me on Medium or Twitter.

--

--