Updating the FormControl value using the ControlValueAccessor

I have developed a directive specifically for case manipulation. The code I created successfully updates the visual value using _Renderer2, but I am facing an issue with formGroup.value. Even though the directive visually changes the value as expected, the original value is retained in the formControl. Consequently, when extracting data from the form using formGroup.value, all values appear in lowercase. Is there a way to address this discrepancy? Thank you

<form [formGroup]="formGroup">
    <mat-form-field>
        <input placeholder="Street" formControlName="streetName" matInput uppercase>
    </mat-form-field>
</form>

import { Directive, ElementRef, Optional, Renderer2, Self } from '@angular/core';
import { ControlValueAccessor, NgControl } from '@angular/forms';

@Directive({
  selector: "textarea[uppercase], input[uppercase]",
  host: {
    '(input)': 'writeValue($event.target.value)',
    '(blur)': 'onTouched()',
  }
})
export class UppercaseDirective implements ControlValueAccessor {

  onChange = (_: any) => {
    console.log("onChange", _)
  };

  onTouched = () => {
    console.log("onTouched")
  };

  constructor(private _renderer: Renderer2, private _elementRef: ElementRef, @Optional() @Self() public ngControl: NgControl) {
    ngControl.valueAccessor = this;
  }

  writeValue(value: any): void {
    this._renderer.setProperty(this._elementRef.nativeElement, 'value', this.transformValue(value));
  }

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

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

  setDisabledState(isDisabled: boolean): void {
    this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
  }

  private transformValue(value: string): string {
    return typeof value === 'string'
      ? value?.toUpperCase()
      : value;
  }
}

Answer №1

It seems like you may be confusing the custom form control concept with directives. One alternative approach is to create a directive that transforms an input by utilizing HTMLEvent. Here's an example:

@Directive({
  selector: "[toUpperCase]"
})
export class ToUpperCaseDirective implements AfterViewInit {
  constructor(private elementRef: ElementRef) {}

  @HostListener("input", ["$event"]) onKeyDown(event: KeyboardEvent) {
    this.setUpper();
  }
  ngAfterViewInit() {
    setTimeout(() => {
      this.setUpper();
    });
  }
  setUpper() {
    const target = this.elementRef.nativeElement;
    let pos = target.selectionStart; //get the position of the cursor
    target.value = target.value.toUpperCase();
    var evt = document.createEvent("HTMLEvents");
    evt.initEvent("input", false, true);
    this.elementRef.nativeElement.dispatchEvent(evt);
    target.selectionStart = target.selectionEnd = pos; //return the position
  }
}

NOTE: Although using a directive for converting input to uppercase may not be the best solution, as it can be achieved more efficiently with CSS:

<input style="text-transform:uppercase">

In this case, you can simply apply CSS styling to transform input values to uppercase.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Initial state was not properly set for the reducer in TypeScript

Encountered an error while setting up the reuder: /Users/Lxinyang/Desktop/angular/breakdown/ui/app/src/reducers/filters.spec.ts (12,9): error TS2345: Argument of type '{}' is not assignable to parameter of type '{ selectionState: { source: ...

Issue: Module compilation unsuccessful (from ./node_modules/css-loader/dist/cjs.js) during upgrade from Angular 11 to Angular 16

Summary: Can anyone help with a strange error I encountered while updating my Angular application? Situation: After finally updating my Angular 11 app to Angular 16, I faced several fixable errors along the way. However, one particular error is now hind ...

Problem with Infragistics radio button not firing change event when value is set manually

After migrating from Angular 11 to 17, I encountered a strange issue with my application's Infragistics radio button. The change event for the radio button does not trigger manually for the first time, but it works fine when changed through the applic ...

How to link a selection choice to an option using Angular 13

Can anyone provide guidance on how to bind data with a select option in Angular? I've tried multiple approaches but haven't been successful in getting the data to display in the options. Any assistance would be greatly appreciated. Thank you in a ...

The latest update of WebStorm in 2016.3 has brought to light an error related to the experimental support for decorators, which may undergo changes in forthcoming

Hello, I recently updated to the latest WebStorm version and encountered this error message: Error:(52, 14) TS1219:Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' ...

Having difficulty sending emails with attachments using AngularJS

While using Angular, I encountered an issue when sending an email with an attachment. The response I received contained the data code of the file instead of its actual format. See example: https://i.stack.imgur.com/vk7X8.png I am unsure what is causing t ...

Error encountered in React TypeScript: Expected symbol '>' was not found during parsing

While transitioning from JavaScript to TypeScript, I encountered an error in my modified code: Error on Line 26:8: Parsing error: '>' expected import React from "react"; import { Route, Redirect, RouteProps } from "react-router ...

Validating minimum and maximum values with Angular 2 FormBuilder

I am currently developing a form using Angular 2 Formbuilder and I want to ensure that users can only input positive values into the amount field (with a minValue of 0 and maxValue of 100). How can I go about implementing minimum and maximum value validati ...

Dynamically render a nested component, created within the parent component, using a directive

Can a nested component be dynamically rendered as a directive within the parent component? Instead of using the standard approach like this: <svg> <svg:g skick-back-drop-item /> </svg> where "skick-back-drop-item" is the s ...

Maximizing Performance of JSON.stringify in Angular

In my Angular app, I have a service that retrieves JSON data from an API. At times, this API sends back over 400 records (JSON Objects). When I directly access the endpoint in my browser, it takes around 5 seconds to load all the JSON data onto the page. ...

Steps for initiating an Angular 4 project

While most developers have moved on to Angular 5, I was tasked with creating a project using Angular 4. After conducting research for several days, I discovered that downgrading the Angular CLI would allow me to accomplish this. By following this approach, ...

Having trouble getting ng-click to function properly in TypeScript

I've been struggling to execute a function within a click function on my HTML page. I have added all the TypeScript definition files from NuGet, but something seems to be going wrong as my Click Function is not functioning properly. Strangely, there a ...

Can you explain the purpose and functionality of the following code in Typescript: `export type Replace<T, R> = Omit<T, keyof R> & R;`

Despite my efforts, I am still struggling to grasp the concept of the Replace type. I have thoroughly reviewed the typescript documentation and gained some insight into what is happening in that line, but it remains elusive to me. ...

Mapping an HTTP object to a model

Currently facing an issue with mapping an http object to a specific model of mine, here is the scenario I am dealing with: MyObjController.ts class MyObjController { constructor ( private myObj:myObjService, private $sco ...

Exploring the mechanics behind ES6 Map shims

From what I've gathered from the documentation (here and here), it seems that having a reference to the memory address is necessary for the operation to work: const foo = {}; const map = new Map(); map.set(foo,'123'); // This action requi ...

What distinguishes between a public variable declared as var: any = []; versus a public variable declared as var: any[] =

I'm seeking clarification on the distinction between the following: public var: any = []; // versus public var: any[] = []; ...

Sending data to the makeStyle function in TypeScript

How can I set the style of backgroundImage in my React component based on the value of post.mainImage? Here is the code snippet: import React from 'react'; import { Post } from '../interfaces'; import { makeStyles, createStyles, Theme ...

Fixing a compare function problem in Angular 6 mat-select

Being currently engrossed in an angular 6 project, I find myself grappling with a mat-select element that necessitates the use of the compareWith function due to my selection of objects. Curiously enough, even when I refrain from selecting any option, it ...

Referring to a component type causes a cycle of dependencies

I have a unique situation where I am using a single service to open multiple dialogs, some of which can trigger other dialogs through the same service. The dynamic dialog service from PrimeNg is being used to open a dialog component by Type<any>. Ho ...

Mapping through multiple items in a loop using Javascript

Typescript also functions Consider an array structured like this const elementList = ['one', 'two', 'three', 'four', 'five'] Now, suppose I want to generate components that appear as follows <div&g ...