Create Custom Form Control for ng-select in Angular

How to Create Custom Form Control in Angular 

Why we create custom form control ? Normally, we used to create custom form control for re-usability. 

The best advantage is that to add common functionality and features in this custom form control and extend the functionality as per our need and requirement. 

And we can use everywhere into the application.

But this example will tell you how to create custom form control to extend the functionality of  angular ng-select component. 

We are adding a feature for this control, multi selection along with checkbox.

Before that, we need to know what is the basic syntax of creating custom form control in angular.

So, we start by implementing the ControlValueAccessor interface from @angular/forms into the component. This interface has three functions that need to be implemented and one optional functional.

writeValue(obj: any): void { }

registerOnChange(fn: any): void { }

registerOnTouched(fn: any): void { }

setDisabledState?(isDisabled: boolean): void { }

The function setDisabledState is optional.

Let's create a example, this example will gives us the idea to how to extend the functionality and re-usability of ng-select component with below steps: 

Step 1 : create a new component with the name of custom-ng-select.component.


Step 2 : add below code to custom-ng-select.component.html

HTML
<ng-select [items]="itemsList" 
           [title]="itemTitle"
           [loading]="!itemsList"
           [bindLabel]="itemLabel" 
           [bindValue]="itemValue"
           [multiple]="isMultiple"
           [disabled]="isDisabled"
           [clearable]="isClearable"
           [searchable]="isSearchable"
           [(ngModel)]="selectedValue"
           [placeholder]="itemPlaceholder" 
           [virtualScroll]="isVirtualScroll"
           [closeOnSelect]="isCloseOnSelect"
           [selectableGroup]="isSelectableGroup"
           (change)="onChange($event)">
    <ng-container *ngIf="(isSelectAll && itemsList != '')">
      <ng-template ng-header-tmp>
        <div class="cursor-pointer">
          <input type="checkbox" [(ngModel)]="isChecked" 
                (change)="checkValue($event)" /> All
        </div>
      </ng-template>
      <ng-template ng-option-tmp let-item="item" let-item$="item$" let-index="index">
        <input id="item-{{index}}" type="checkbox" [ngModel]="item$.selected" /> 
            {{item[itemLabel]}}
      </ng-template>
    </ng-container>
</ng-select>


Step 3 :  add below code to this custom-ng-select.component.ts

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

@Component({
  selector: 'app-custom-ng-select',
  templateUrl: './custom-ng-select.component.html',
  styleUrls: ['./custom-ng-select.component.scss'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomNgSelectComponent),
    multi: true
  }]
})

export class CustomNgSelectComponent implements ControlValueAccessor {
  public isChecked = false;
  private innerValue: any = '';
  public touchedFunction: () => void;
  public changeFunction: (value: any) => void;

  @Input() public itemTitle = '';
  @Input() public itemsList: any;
  @Input() public itemLabel: string;
  @Input() public itemValue: string;
  @Input() public isClearable = true;
  @Input() public isMultiple = false;
  @Input() public isDisabled = false;
  @Input() public isSelectAll = false;
  @Input() public isSearchable = false;
  @Input() public isVirtualScroll = false;
  @Input() public isCloseOnSelect = false;
  @Input() public itemPlaceholder: string;
  @Input() public isSelectableGroup: false;

  @Output() public changeEventHandler: EventEmitter = new EventEmitter();

  public checkValue(event: any): void {
    if (this.isChecked) {
      this.selectedValue = this.itemsList.map(itemList => itemList[this.itemValue]);
    } else {
      this.selectedValue = [];
    }
  }

  public onChange(event: any): void {
    if (this.selectedValue === [] || this.selectedValue.length === 0
      || this.selectedValue.length !== this.itemsList.length) {
      this.isChecked = false;
    } else if (this.selectedValue.length === this.itemsList.length) {
      this.isChecked = true;
    }
    this.changeEventHandler.emit();
  }

  public get selectedValue(): any {
    return this.innerValue;
  }

  public set selectedValue(value: any) {
    if (value !== this.innerValue) {
      this.innerValue = value;
      this.changeFunction(value);
    }
  }

  public writeValue(value: any): void {
    // tslint:disable-next-line: triple-equals
    if (value !== null && value !== undefined && value != '') {
      this.innerValue = value;
    } else {
      this.isChecked = false;
      this.innerValue = [];
    }
  }

  public registerOnChange(fn: () => void) {
    this.changeFunction = fn;
  }

  public registerOnTouched(fn: () => void) {
    this.touchedFunction = fn;
  }

  public setDisabledState?(isDisabled: boolean): void { }

}

We have successfully created the custom form control component added with some additional functionality to them.

Let's use this custom form control into application.

Step 1 : add code in app.component.html

HTML
<div style="width: 600px;">
  <app-custom-ng-select [itemsList]="carsInformation" [itemLabel]="'name'" [itemValue]="'id'"
    [itemPlaceholder]="'Select Car'" [(ngModel)]="selectedCar" [isSearchable]="true" 
    [isSelectableGroup]="false" [isCloseOnSelect]="true" [isMultiple]="true" [isSelectAll]="true">
  </app-custom-ng-select>
</div>

Step 2 : add code in app.component.ts

Typescript
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  public title = 'demo-app';
  public selectedCar: string;
  public carsInformation: any;

  public ngOnInit(): void {
    setTimeout(() => {
      this.carsInformation = [
        { id: 1, name: 'Ford Mustang (1964-Present)' },
        { id: 2, name: 'Rolls-Royce Silver Cloud (1955-1966)' },
        { id: 3, name: 'Ferrari Testarossa (1984-1996)' },
        { id: 4, name: 'Lamborghini Countach (1974-1990)' },
        { id: 5, name: 'Dodge Viper (1992-Present)' },
        { id: 6, name: 'Chevrolet Corvette (1953-Present)' },
        { id: 7, name: 'Bugatti Veyron (2006-Present)' },
        { id: 8, name: 'Porsche Carrera 1956-Present' }
      ]
    }, 5000);
  }

}


Step 3 : add code in app.module.ts

Typescript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CustomNgSelectComponent } from './custom-ng-select/custom-ng-select.component';
import { CommonModule } from '@angular/common';
import { NgSelectModule } from '@ng-select/ng-select';
import { FormsModule } from '@angular/forms';

@NgModule({
    declarations: [
        AppComponent,
        CustomNgSelectComponent
    ],
    imports: [
        BrowserModule,
        AppRoutingModule,
        CommonModule,
        NgSelectModule,
        FormsModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})

export class AppModule { }


Step 4 : add code in styles.scss

CSS
/* You can add global styles to this file, and also import other style files */
@import "~@ng-select/ng-select/themes/default.theme.css";

.divcenter {
    display: flex;
    align-items: center;
    justify-content: center;
}


Expected Result Should like below :



Comments

  1. Good tutorial. I am trying to add validation to this but the validation classes do not propagate to the parent. Any idea why?

    ReplyDelete
  2. From my deepest point of my heart I would like to say thank you, you are the real super man!

    ReplyDelete

Post a Comment

Popular posts from this blog

Send API POST Request in MS SQL Server

How to Convert JSON Object to XML in C#

Tightly Coupled and Loosely Coupled in .NET Core with example

Restrict Special Characters Using JavaScript By Copy,Paste

Delete Empty Rows or Null Rows in Datatable in C#

Create Extension Method in typescript - String.IsNullOrEmpty()

Domain Driven Design (DDD) in .NET Core

Configure the API gateway in Microservices Architecture with example in .NET Core

Javascript Hoisting