DEV Community

Nikhil Sai
Nikhil Sai

Posted on • Updated on

Create Custom Form Control in Angular

Angular provides a powerful way to create custom form controls to enhance the functionality and reusability of your application. In this article, we'll walk through the process of creating a custom form control step by step, with a detailed explanation, background on NG_VALUE_ACCESSOR, and a practical example.

Background on NG_VALUE_ACCESSOR

In Angular, NG_VALUE_ACCESSOR is a special injection token used to register and provide a way for Angular forms to communicate with custom form controls. It's a crucial part of creating custom form controls because it allows Angular to integrate them seamlessly into the reactive forms framework.

When you define a custom form control that implements the ControlValueAccessor interface and provide it using NG_VALUE_ACCESSOR, you're essentially telling Angular how to interact with your custom control. This includes syncing values, handling changes, and setting up two-way data binding.

ControlValueAccessor Methods

The ControlValueAccessor interface defines several methods that you must implement in your custom form control class. These methods enable the interaction between your custom control and Angular forms:

interface ControlValueAccessor<T> {
  writeValue(obj: T): void;
  registerOnChange(fn: (value: T) => void): void;
  registerOnTouched(fn: () => void): void;
  setDisabledState?(isDisabled: boolean): void;
}
Enter fullscreen mode Exit fullscreen mode
  1. writeValue(value: any): This method is used to write a value from the model into the view. It's called when you programmatically set the value of your custom form control. Inside this method, you typically update the internal state of your control.

  2. registerOnChange(fn: any): This method registers a callback function that Angular calls whenever the value of your custom control changes. You should store this callback and call it when your control's value changes to notify Angular.

  3. registerOnTouched(fn: any): This method is similar to registerOnChange, but it's used to register a callback function that Angular calls when the custom control is "touched" or loses focus. It's typically used to mark the control as touched and trigger validation.

  4. setDisabledState(isDisabled: boolean): This optional method allows you to set the disabled state of your custom control. It's useful when you want to enable or disable the control programmatically.

Now we will see how we can create a Poll form control using the concept we have learnt.

Detailed Steps to Create a Custom Poll Control

Prerequisites

Before we dive into creating custom form controls, make sure you have the following prerequisites installed:

  1. Node.js and npm (Node Package Manager)
  2. Angular CLI (Command Line Interface) - You can install it globally using npm install -g @angular/cli.

Step 1: Set Up the Angular Project

If you haven't already, create a new Angular project using the Angular CLI:

ng new custom-poll-control-example
Enter fullscreen mode Exit fullscreen mode

Navigate to the project directory:

cd custom-poll-control-example
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a Custom Poll Control

Now, create a custom poll control component using the Angular CLI:

ng generate component poll-control
Enter fullscreen mode Exit fullscreen mode

Step 3: Implement the Custom Poll Control with Value Accessor Methods

In the poll-control component, open the poll-control.component.ts file, and define the custom form control with value accessor methods:

import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-poll-control',
  templateUrl: './poll-control.component.html',
  styleUrls: ['./poll-control.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => PollControlComponent),
      multi: true,
    },
  ],
})
export class PollControlComponent implements ControlValueAccessor, OnInit {
  @Input() pollOptions: string[]; // Input property to receive poll options
  selectedOptions: string[] = [];
  onChange: any = () => {};
  onTouched: any = () => {};

  constructor() {}

  ngOnInit() {}

  writeValue(value: string[]) {
    this.selectedOptions = value;
  }

  registerOnChange(fn: any) {
    this.onChange = fn;
  }

  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean) {
    // Implement if needed
  }

  updateSelectedOptions(option: string) {
    if (this.isSelected(option)) {
      this.selectedOptions = this.selectedOptions.filter((item) => item !== option);
    } else {
      this.selectedOptions.push(option);
    }
    this.onChange(this.selectedOptions);
    this.onTouched();
  }

  isSelected(option: string) {
    return this.selectedOptions.includes(option);
  }
}
Enter fullscreen mode Exit fullscreen mode

In this code, we implement the ControlValueAccessor interface with methods like writeValue, registerOnChange, and registerOnTouched. We also create a method updateSelectedOptions to handle checkbox changes and update the form control value.

Step 4: Create the Custom Poll Control Template

In the poll-control component directory, open the poll-control.component.html file, and create the HTML template for the poll control:

<div>
  <div *ngFor="let option of pollOptions">
    <label>
      <input
        type="checkbox"
        [checked]="isSelected(option)"
        (change)="updateSelectedOptions(option)"
      />
      {{ option }}
    </label>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Step 5: Using the Custom Poll Control

In a parent component (e.g., app.component.html), use the custom poll control to create and display a poll:

<form [formGroup]="pollForm">
  <app-poll-control [pollOptions]="options" formControlName="pollOptions"></app-poll-control>
  <button (click)="submitPoll()">Submit Poll</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Step 6: Handling the Poll Data

In your parent component (e.g., app.component.ts), define a FormGroup for the poll form, handle the submitted data, and set up the form control:

import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  options: string[] = ['BreakFast', 'Lunch', 'Dinner'];
  pollForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.pollForm = this.fb.group({
      pollOptions: [[]], // Initialize with an empty array
    });
  }

  submitPoll() {
    const selectedOptions = this.pollForm.value.pollOptions;
    console.log('Selected Poll Options:', selectedOptions);
  }
}
Enter fullscreen mode Exit fullscreen mode

Output

UI

Console Log

Conclusion

In this example, we created a custom poll control in Angular using the ControlValueAccessor interface. Users can select multiple options, and the parent component can handle the selected options. This demonstrates how you can create more complex custom form controls in Angular to suit your application's requirements. You can further enhance this example by adding features like dynamic poll creation and result display.

Thank's for your time, see you soon :)

Top comments (0)