javascriptangulartypescriptangular-formshtml-input

How to filter user input in Angular?


I want to filter user input when they type in an HTML text input.

I can do that in native HTML/JavaScript as shown in the following demo:

<form>
    <label for="tracking">Tracking Number:</label>
    <input
        type="text"
        id="tracking"
        name="tracking"
        pattern="^[A-Z]{2}\d{9}[A-Z]{2}$"
        title="Format must be like AB123456789CD"
        required
        minlength="13"
        maxlength="13"
    />
</form>

<script>
    const input = document.getElementById('tracking');

    input.addEventListener('input', () => {
        console.log('input fired');

        // Remove non-alphanumeric chars and force uppercase
        input.value = input.value.replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
    });
</script>

filtering user input with HTML/JS

In the image above, I'm typing a, b, +, - (filtering works like I want). StackBlitz demo: Filter user input (native)

Now, I've done the same thing using Angular (with a template-driven form) as shown in the following demo:

@Component({
  selector: 'app-root',
  template: `
    <form>
      <label for="tracking">Tracking Number:</label>
      <input
        type="text"
        id="tracking"
        name="tracking"
        pattern="^[A-Z]{2}\d{9}[A-Z]{2}$"
        title="Format must be like AB123456789CD"
        required
        minlength="13"
        maxlength="13"
        [ngModel]="trackingNumber"
        (ngModelChange)="onTrackingChange($event)"
      />
    </form>
  `,
  imports: [FormsModule],
})
export class App {
  trackingNumber = '';

  onTrackingChange(value: string) {
    console.log('input fired');

    // Remove non-alphanumeric characters and force uppercase
    this.trackingNumber = value.replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
  }
}

filtering user input with Angular

In the image above, I'm typing a, b, +, - (filtering does NOT work like I want). StackBlitz demo: Filter user input (Angular)

As far as my Angular knowledge goes, this happens when the current ngModel value is the same as the new/filtered value, thus Angular does not trigger a change on the HTML text input.

How can I overcome this behavior in Angular?

Can I force Angular to trigger a change?


Solution

  • The user "novative" on the Angular discord found a solution by replacing (ngModelChange) with (input) + manually updating the input value.

    @Component({
      selector: 'app-root',
      template: `
        <form>
          <label for="tracking">Tracking Number:</label>
          <input
            type="text"
            id="tracking"
            name="tracking"
            pattern="^[A-Z]{2}\d{9}[A-Z]{2}$"
            title="Format must be like AB123456789CD"
            required
            minlength="13"
            maxlength="13"
            [ngModel]="trackingNumber"
            (input)="onTrackingChange(box)" 
            #box
          />
        </form>
      `,
      imports: [FormsModule],
    })
    export class App {
      trackingNumber = '';
    
      onTrackingChange(box: HTMLInputElement) {
        console.log('input fired');
    
        // Remove non-alphanumeric characters and force uppercase
        this.trackingNumber = box.value = box.value.replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
      }
    }
    

    Thanks to anyone who tried to help!