How to Convert NgModel / FormControl Input Text to Title Case using Angular Directive and Angular Pipe

How to Convert NgModel / FormControl Input Text to Title Case using Angular Directive and Angular Pipe

You can easily convert the text in input fields to title case using Angular TitleCase Pipe along with the power of Angular Directives. In this tutorial, I am going to show you how to create a reusable TitleCase directive, which converts the text entered by the user to Title Case.

The first section of this post will show you how to make use of Angular built-in TitleCase pipe. The second section of this post will teach you how to write our own logic to convert the text to title case.

Create TitleCase Directive

Create a new TitleCase directive using the following command

ng generate directive title-case

Add the following HTML code in your app.component.html file.

<input type="text" appTitleCase [(ngModel)]="firstname" name="firstname"/>

We have added the appTitleCase selector to the input field which is our title case directive.

1. Using Angular TitleCase Pipe

From Angular 4, Angular has a built-in pipe for Title Case called TitleCase. You can use it to transform data in your template as follows:

<h1>{{ hello world | titlecase }}</h2>
<!-- output is expected to be "Hello World" -->

It’s very easy to transform the data in your HTML template into title case using the TitleCase pipe. But, you can’t use this method to transform the data in your input fields. In other words, you can’t write something like this: [ngModel]="modelName | titlecase" or [formControl]="controlName | titlecase"

To solve this problem, we are going to use the TitleCase pipe inside our newly created TitleCase Directive.

How to Access Angular Pipe Inside Angular Directive

To access the pipe inside the angular directive, you should first provide the pipe in the providers array of your module. You can import the built-in TitleCasePipe from @angular/common package.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TitleCasePipe } from '@angular/common';
import { TitlecaseDirective } from './titleCase.directive';

@NgModule({
  declarations: [
    AppComponent,
    TitlecaseDirective 
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule
  ],
  providers: [TitleCasePipe], // provide titlecase pipe to use it in directive.ts and component.ts
  bootstrap: [AppComponent]
})
export class AppPatientModule { }

Now you can import TitleCasePipe into the constructor of your newly created TitleCaseDirective. You should also import NgControl class to the constructor.

What is NgControl?

NgControl is the base class for all the form controls. In our case, Once we transformed the data to title case we will update the value of the form control using NgControl.valueAccessor. The valueAccessor has a method called writeValue which is used to update the value of the form control.

import { Directive, HostListener } from '@angular/core';
import { NgControl } from '@angular/forms';
import { TitleCasePipe } from '@angular/common';
@Directive({
  selector: '[appTitleCase]'
})
export class TitlecaseDirective {

  constructor(
    public ngControl: NgControl,
    public titleCase: TitleCasePipe
  ) { }

}

Listen For Input Change

To listen for the input change in our directive, we should use @HostListener('ngModelChange', ['$event']) . The ngModelChange triggers whenever the user changes the input field text (ngModel/formControl).

@HostListener('ngModelChange', ['$event'])
onInputChange(value) {
   // whenever user types something in the input field, this function triggers.
}

Convert To Title Case using TitleCasePipe

The TitleCasePipe has a transform method which has the logic to convert the data to title case. We assign the returned value to a variable called titleCaseStr. Then, we should update the value of the form control using the writeValue method of valueAccessor.

@HostListener('ngModelChange', ['$event'])
onInputChange(value) {
   const titleCaseStr = this.titleCase.transform(value);
   this.ngControl.valueAccessor.writeValue(titleCaseStr);
}

2. Write Your Own Title Case Conversion Logic

If you are like me, to dig deeper into the logic and understand what happens under the hood rather than just using the API. This section is for you.

There are multiple ways to convert the string to title case in JavaScript. In this section, I will teach you three ways of title case conversion.

  • Using for…of loop
  • Using the Array map method
  • Using the Array reduce method

Convert Text to Title Case Using for…of loop

  1. Declare a variable called arrStr.
  2. Convert the incoming value to lower case and split the string into an array. Assign the returned array to the arrStr variable.
  3. Declare a variable called titleCaseArr with a default value as an array.
  4. Write a for of loop, access the first character of the string using charAt method and convert the first letter to uppercase.
  5. Select the remaining characters of the string using slice(1) method.
  6. Concatenate the string and push it to the titleCaseArr array.
  7. Convert the titleCaseArr to string using the join method and update the form field using writeValue method.
@HostListener('ngModelChange', ['$event'])
onInputChange(value) {
  const arrStr = value.toLowerCase().split(' ');
  const titleCaseArr: Array<any> = [];

  for (const str of arrStr) {
     titleCaseArr.push(str.charAt(0).toUpperCase() + str.slice(1));
  }
  
  this.ngControl.valueAccessor.writeValue(titleCaseArr.join(' '));
}

Convert text to Title Case using Array Map method

  1. Declare a variable called arrStr.
  2. Convert the incoming value to lower case and split the string into an array. Assign the returned array to the arrStr variable
  3. Declare a variable called titleCaseStr.
  4. Use the map method in arrStr, access the first character of the string using charAt method and convert the first letter to uppercase.
  5. Select the remaining characters of the string using slice(1) method.
  6. Concatenate the first letter and remaining letters and return it.
  7. Chain the join method after the map method to convert the array to string.
  8. Update the form field using writeValue method.
@HostListener('ngModelChange', ['$event'])
onInputChange(value) {
  const arrStr = value.toLowerCase().split(' ');
  const titleCaseStr = arrStr.map((str) => (str.charAt(0).toUpperCase() + str.slice(1))).join(' ');
  
  this.ngControl.valueAccessor.writeValue(titleCaseStr);
}

Convert text to Title Case using Array Reduce method

  1. Declare a variable called arrStr.
  2. Convert the incoming value to lower case and split the string into an array. Assign the returned array to the arrStr variable
  3. Declare a variable called titleCaseStr.
  4. Use the reduce method in arrStr.
  5. Declare a variable inside the reduce method called spaceBetweenWords , If the first parameter of the reduce method which is the accumulated value called accumulatedStr is not equal to an empty string, then we assign space to the spaceBetweenWords
  6. Access the first character of the string using charAt method and convert the first letter to uppercase.
  7. Select the remaining characters of the string using slice(1) method.
  8. Concatenate the first letter and remaining letters and return it.
  9. Update the form field using writeValue method.
@HostListener('ngModelChange', ['$event'])
onInputChange(value) {
  const arrStr = value.toLowerCase().split(' ');
  const titleCaseStr = arrStr.reduce((accumulatedStr, currentStr) => {
        const spaceBetweenWords = (accumulatedStr ? ' ' : '');
        return accumulatedStr += spaceBetweenWords + (currentStr.charAt(0).toUpperCase() + currentStr.slice(1));
   }, '');
  
  this.ngControl.valueAccessor.writeValue(titleCaseStr);
}



FULL CODE EXAMPLE FOR ANGULAR TITLE CASE DIRECTIVE


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TitleCasePipe } from '@angular/common';
import { TitlecaseDirective } from './titleCase.directive';

@NgModule({
  declarations: [
    AppComponent,
    TitlecaseDirective 
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule
  ],
  providers: [TitleCasePipe], // provide titlecase pipe to use it in directive.ts and component.ts
  bootstrap: [AppComponent]
})
export class AppModule { }


import { Directive, HostListener } from '@angular/core';
import { NgControl } from '@angular/forms';
import { TitleCasePipe } from '@angular/common';
@Directive({
  selector: '[appTitleCase]'
})
export class TitlecaseDirective {

  constructor(
    public ngControl: NgControl,
    public titleCase: TitleCasePipe
  ) { }

  @HostListener('ngModelChange', ['$event'])
  onInputChange(value) {

    if (value && value !== '' && value.length > 0) {
      const arrStr = value.toLowerCase().split(' ');

      titleCaseStr = this.titleCase.transform(value);
      this.ngControl.valueAccessor.writeValue(titleCaseStr);
    }
  }
}


import { Directive, HostListener } from '@angular/core';
import { NgControl } from '@angular/forms';
import { TitleCasePipe } from '@angular/common';
@Directive({
  selector: '[appTitleCase]'
})
export class TitlecaseDirective {

  constructor(
    public ngControl: NgControl,
    public titleCase: TitleCasePipe
  ) { }

  @HostListener('ngModelChange', ['$event'])
  onInputChange(value) {
    const arrStr = value.toLowerCase().split(' ');
    const titleCaseArr: Array = [];

    for (const str of arrStr) {
      titleCaseArr.push(str.charAt(0).toUpperCase() + str.slice(1));
    }
  
    this.ngControl.valueAccessor.writeValue(titleCaseArr.join(' '));
  }
}


import { Directive, HostListener } from '@angular/core';
import { NgControl } from '@angular/forms';
import { TitleCasePipe } from '@angular/common';
@Directive({
  selector: '[appTitleCase]'
})
export class TitlecaseDirective {

  constructor(
    public ngControl: NgControl,
    public titleCase: TitleCasePipe
  ) { }

  @HostListener('ngModelChange', ['$event'])
  onInputChange(value) {
    const arrStr = value.toLowerCase().split(' ');
    const titleCaseStr = arrStr.map((str) => (str.charAt(0).toUpperCase() + str.slice(1))).join(' ');
  
    this.ngControl.valueAccessor.writeValue(titleCaseStr);
  }
}


import { Directive, HostListener } from '@angular/core';
import { NgControl } from '@angular/forms';
import { TitleCasePipe } from '@angular/common';
@Directive({
  selector: '[appTitleCase]'
})
export class TitlecaseDirective {

  constructor(
    public ngControl: NgControl,
    public titleCase: TitleCasePipe
  ) { }

  @HostListener('ngModelChange', ['$event'])
  onInputChange(value) {
    const arrStr = value.toLowerCase().split(' ');
    const titleCaseStr = arrStr.reduce((accumulatedStr, currentStr) => {
        const spaceBetweenWords = (accumulatedStr ? ' ' : '');
        return accumulatedStr += spaceBetweenWords + (currentStr.charAt(0).toUpperCase() + currentStr.slice(1));
     }, '');
  
    this.ngControl.valueAccessor.writeValue(titleCaseStr);
  }
}


<input type="text" appTitleCase [(ngModel)]="firstname" name="firstname"/>