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

Issue with the Angular source maps are causing the sourceMappingUrl to break

After upgrading from Angular 7 to Angular 8, I've encountered an issue in Chrome and Firefox where there is an error in the dev console In Firefox: Error: JSON.parse: unexpected character at line 1 column 1 of the JSON data URL for Source Map: [url ...

Ways to conceal a table and button in the absence of data for display

I've been working on a way to hide the table and the 'changeState' button when there's no data present. Currently, I have set it up so that a message saying 'No entries in the list!' pops up briefly before disappearing, bringi ...

Nativescript encountered an error regarding faker: module './address' not found

Currently in the process of learning nativescript, I am experimenting with using faker to generate some data while working with typescript. Here are the versions I am using: Node - 6.9.4 Faker - 3.1.0 Typescript - 2.1.4 Encountered an error which i ...

Filtering an array using criteria: A step-by-step guide

Currently, I am developing a system for Role Based permissions that involves working with arrays. Here is an example of the array structure I have: let Roles = { [ { model: 'user', property: 'find', permission: 'allow' ...

Prevent automatic suggestions from appearing in Ionic app on Android

Our Ionic v4 (Angular, Cordova) app features form input fields (<ion-input>) like the example below: <ion-input formControlName="firstName" type="text" inputmode="text" autocapitalize="sentences"></ion-input> When running the Android ap ...

Tips for making an ajax call in Angular 6

As a backend developer, I have limited experience with Angular. How can I send an ajax request from Angular to test my API? The request needs to be sent before clearing the localeStorage. Can you provide guidance on how to achieve this? <button (clic ...

Typescript constructor that accepts an object as an argument instead of traditional parameters

My constructor is becoming lengthy and not structured the way I would prefer. I am looking to pass an object to my constructor so that I can access fields by their names. Here is how the class looks currently. export class Group { id: string; constru ...

Set up a global variable for debugging

Looking to include and utilize the function below for debugging purposes: export function debug(string) { if(debugMode) { console.log(`DEBUG: ${string}`) } } However, I am unsure how to create a globally accessible variable like debugMode. Can this be ...

Issue with triggering blur event in Internet Explorer while using Angular 2+

The issue discussed in the Blur not working - Angular 2 thread is relevant here. I have a custom select shared component and I am attempting to implement a blur event to close it when the component loses focus. // HTML <div (blur)="closeDropDown()" t ...

Transforming a function into an array in TypeScript

I attempted to use the map() function on a dataURL array obtained from the usePersonList() hook, but I am struggling to convert my function to an array in order to avoid errors when clicking a button. import Axios from "axios"; import React, { us ...

Is there a way to apply a decorator to a function that has been returned?

Can the following be accomplished? bar () { @custom yield () => { } } ...

How do I determine if a child component is in a dirty state within CanDeactivateGuard when dealing with multiple form tags?

Currently, I am utilizing a template driven form within my project. The parent component that I am working on is as follows: parent.component.html <tab> <form> <input></input> <button></button> </form ...

Restrict the number of subscriptions allowed for an rxjs observable

Seeking a replacement for observable, subject, or event emitter that allows only one subscription at a time. The first subscriber should have priority to execute its subscribe function, with subsequent subscribers waiting their turn until the first unsubsc ...

Getting the hang of using and translating typescript .tsx files with jsx in React-native

Recently, I have ventured into the world of React-native after having experience with reactjs+typescript. Wanting to test its capabilities, I decided to set up a simple project. My tool of choice for development is VS Code. After following a basic tutoria ...

The ASP .Net Core 3.1 Angular template is malfunctioning

https://i.sstatic.net/T3hFx.pngAfter creating a new Web Project using the .Net Core with Angular template in Visual Studio 2019, I encountered an error when trying to build the solution. The error message stated: An unhandled exception occurred while proce ...

The React Native blob or file seems to be encountering trouble in reaching the server

I'm in a tough spot here. I can't figure out what's going wrong. My goal is to send a file using the expo-image-picker component to the server. The form gets sent, but the image doesn't. When I use the fetch command, I immediately get a ...

Retrieving attributes by their names using dots in HTML

Currently working on an Angular 2 website, I am faced with the challenge of displaying data from an object retrieved from the backend. The structure of the object is as follows: { version: 3.0.0, gauges:{ jvm.memory.total.used:{ value: 3546546 }}} The is ...

Tips for streamlining a conditional statement with three parameters

Looking to streamline this function with binary inputs: export const handleStepCompletion = (userSave: number, concur: number, signature: number) => { if (userSave === 0 && concur === 0 && signature === 0) { return {complet ...

When transitioning from the current step to the previous step in the mat-stepper component, how can we emphasize the horizontal line that separates them?

screenshot of my progress I have progressed from A3 to A2 step, but I need to add a horizontal line between them. How can I achieve this and is it possible to do so using CSS styles? ...

Using the useContext hook across multiple files without needing to export it

I am working on a React app that has multiple states being managed function App(){ const [activeChoice, setActiveChoice] = useState("flights"); const [overlay, setOverlay] = useState(false); const [airports, setAirports] = useState([]); const [loading, ...