Angular: directive modifying the value of a form control

I have implemented a feature to automatically transform the slug when the user inputs the name in a control field. This transformation includes changing spaces to hyphens (-) among other things.

Although I created a directive for this purpose, it seems to only work partially. While it successfully transforms the value for the slug input field, it doesn't update the value for the slug form control when typing in the name.

Here is the live code showcasing the issue: https://stackblitz.com/edit/angular-slug-transform

Directive:

import { Directive, HostListener } from "@angular/core";
import { NgControl } from "@angular/forms";

@Directive({
  selector: "[formControlName][appSlugTransform]"
})
export class SlugTransformDirective {
  constructor(public ngControl: NgControl) {}

  @HostListener("ngModelChange", ["$event"])
  onModelChange(event) {
    let newVal = this.transform(event);
    this.ngControl.valueAccessor.writeValue(newVal);
  }

  transform(value) {
    let text = value.toLowerCase();
    if (text.charAt(0) == " ") {
      text = text.trim();
    }
    if (text.charAt(text.length - 1) == "-") {
      //text = (text.replace(/-/g, ""));
    }
    text = text.replace(/ +/g, "-");
    text = text.replace(/--/g, "-");
    text = text.normalize("NFKD").replace(/[\u0300-\u036f]/g, ""); // Note: Normalize('NFKD') used to normalize special alphabets like óã to oa
    text = text.replace(/[^a-zA-Z0-9 -]/g, "");

    return text;
  }
}

The issue can be observed in the example as follows:

  • When typing in the name input field, the slug gets transformed in its input field but not in its form control value.
  • Conversely, when typing in the slug input field, both the field and control value are correctly transformed.

Answer №1

Finally overcome the challenge and witness the magic unfold

ngOnInit(): void {
  this.valueSubscription = this.ngControl.control.valueChanges.subscribe(
    value => {
      const newVal = this.transform(value);
      this.ngControl.control.setValue(newVal, { emitEvent: false });
    }
  );
}

Instead of utilizing HostListener to track value changes, I have opted for valueChanges.

For updating the value, I now employ the setValue method of control.

Take a look at the live code here: https://stackblitz.com/edit/angular-slug-directive

Answer №2

  1. Include the Directive within the Name FormControl,
  2. Access the Control Container from the Directive
  3. Retrieve the Slug's control and update its value using patchValue method.
  4. Omit the ("name").onValueChanges() as it will be managed by the directive.

https://stackblitz.com/edit/angular-slug-transform-rhftys

Answer №3

Issue arises from

this.ngControl.valueAccessor.writeValue(newVal)
not initiating the model update. The reason for this behavior is unclear, but an alternative method involving ElementRef & dispatchEvent can be used to trigger the model update:

Initially update the input value using ElementRef and then utilize dispatchEvent.

this.el.value = newVal;
this.el.dispatchEvent(new Event('input'))

demo: https://stackblitz.com/edit/angular-slug-transform-sdmeto



Revised directive code:

import { Directive, HostListener, ElementRef, OnInit } from "@angular/core";
import { NgControl } from "@angular/forms";

@Directive({
  selector: "[formControlName][appSlugTransform]"
})
export class SlugTransformDirective implements OnInit {
  private el: any;

  constructor(public ngControl: NgControl, private elementRef: ElementRef) {
    this.el = this.elementRef.nativeElement;
  }

  ngOnInit() {
    this.el.value = this.transform(this.el.value);
  }

  @HostListener("ngModelChange", [ "$event"])
  onNgModelChange(event) {
    let newVal = this.transform(event);
    // this.ngControl.valueAccessor.writeValue(newVal);
    this.el.value = newVal;
    this.el.dispatchEvent(new Event('input'))
  }

  transform(value) {
    let text = value.toLowerCase();
    if (text.charAt(0) == " ") {
      text = text.trim();
    }
    if (text.charAt(text.length - 1) == "-") {
      //text = (text.replace(/-/g, ""));
    }
    text = text.replace(/ +/g, "-");
    text = text.replace(/--/g, "-");
    text = text.normalize("NFKD").replace(/[\u0300-\u036f]/g, ""); // Note: Normalize('NFKD') used to normalize special alphabets like óã to oa
    text = text.replace(/[^a-zA-Z0-9 -]/g, "");

    return text;
  }
}

An additional technique to consider is utilizing patchValue instead of

writeValue</code as shown below:</p>

<pre><code>this.ngControl.control.patchValue(newVal);

This should facilitate the model change. (untested)

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

Customize back button functionality in Ionic 2

Is it possible to modify the behavior of the back button shown in this image? I would like to specify a custom destination or perform an action before navigating back, instead of simply returning to the previous page. https://i.stack.imgur.com/EI2Xi.png ...

Error: Discord Bot is unable to locate module './commands/ban.js'

Here is my code snippet: require('dotenv').config(); const fs = require('fs'); const Discord = require('discord.js'); const { error } = require('console'); const client = new Discord.Client(); client.commands = new D ...

Set a maximum limit for the number of checkboxes that can be selected

If there are 10 checkboxes on a page, I am searching for a specific behavior: Use a variable called maxSelections to control the maximum number of selectable checkboxes Once maxSelections is reached, disable the other checkboxes on the page Even after re ...

Choose a table by using jQuery excluding the data in the first column

Does anyone know how to extract data from an HTML table using jQuery? In my specific case, I need to retrieve the table contents without including the first column. This is because I want to export the table to an Excel file, but the first column contains ...

Steps for displaying a new event on a fullCalendar

Utilizing fullCalendar to display a list of events in the following manner: this.appointments = [{ title: "Appointment 1", date: "2020-09-06", allDay: false }, { title: "Appointment 2", date: "2020 ...

Using Angular 5 to showcase multiple charts with Chart.js, each chart appearing on its own tab

I have successfully displayed a single chart, but I am facing an issue while trying to display that chart for each tab of a mat-tab-group with different data on each tab. The chart is a part of a larger dashboard consisting of multiple charts, and I have i ...

Tips on managing the issue of "Preflight request response does not meet access control requirements: 'Access-Control-Allow-Credentials' value"

I am attempting to send an HTTP POST request to a web-api2 server running on my localhost. My client is operating on http://localhost:4200, and my server is running on http://localhost/MemoryGameServer/api/Test (Different Origin). Below is my Angular 7 cl ...

What exactly does the .proxy() method do in jQuery?

Can you explain the purpose of the jQuery.proxy function in jQuery and describe the scenarios where it is most beneficial? I came across this link, but I'm struggling to grasp its concept fully. ...

Creating a webpage that automatically clicks on a specific category from a selection using JavaScript and CSS

My latest project involves a page filled with divs, each containing specific text content. Located at the top of the page are buttons that enable filtering of the displayed divs based on their data-category. For instance, I have arranged divs showcasing al ...

Activate the counter as soon as the error message appears

To effectively track the number of errors generated upon form submission, I require a counter mechanism. The errors are identified by the CSS class .validmissing. For instance, if a user encounters 5 errors upon submitting the form, the counter should be ...

Combining the power of Vuforia with the capabilities of Three

I'm having difficulty with the integration of three.js and Vuforia, as I have limited knowledge about OpenGl which makes it challenging to troubleshoot. Our team is working on a small AR app where we aim to utilize Vuforia for tracking and recognitio ...

Altering the background color of an individual mat-card component in an Angular application

Here is the representation of my mat-card list in my Angular application: <div [ngClass]="verticalResultsBarStyle"> <div class="innerDiv"> <mat-card [ngClass]="barStyle" *ngFor="let card of obs | async | paginate: { id: 'paginato ...

Obtaining the value of dynamically generated elements using JavaScript within PHP scripting

Using JavaScript, I dynamically created some textboxes. Below is the JavaScript code: $(document).on('click','#AddHaContactNumberButton',function(e){ e.preventDefault(); var outerDiv = document.getElementById("HaContactDiv"); var textb ...

Issue with Data Table not updating after saving

I have a requirement to refresh my table upon saving. It works fine for a regular table with *ngFor, but I am using the Smartadmin Angular template. I believe the solution involves using table.ajax.reload(), but how can I execute this in an Angular-friendl ...

Error in React: Trying to access property 'functionName' of an undefined object on click event

I am facing an issue while trying to click a button in my React component that is supposed to trigger a function with a parameter named "item" which is defined within the function. The pseudo-HTML snippet for this scenario looks like: <div>{item.cre ...

Utilizing WebWorkers with @mediapipe/tasks-vision (Pose Landmarker): A Step-by-Step Guide

I've been experimenting with using a web worker to detect poses frame by frame, and then displaying the results on the main thread. However, I'm encountering some delays and synchronization issues. My setup involves Next.js 14.0.4 with @mediapip ...

How do I remove all elements from the Canvas in R3F?

I need a way to clear all models from the Canvas with just one click and then switch over to a new Canvas. I want to make sure that all items are removed from memory before making the change. Is there a way to accomplish this? return ( <div clas ...

Inconsistencies with Kendo UI and jquery scrollTop functionality on mobile browsers such as iOS and Android are

While using the kendo ui - grid control, I encountered an issue where resetting the data source did not automatically bring the grids scroll position back to the top. To address this, I tried adding a jquery scrollTop(0) call after refreshing the datasour ...

Unable to retrieve data from file input using jQuery due to undefined property "get(0).files"

I am facing an issue with retrieving file data in jQuery AJAX call from a file input on an ASP.NET view page when clicking a button. HTML <table> <td style="text-align:left"> <input type="file" id="AttachmenteUploadFile" name="Attachme ...

Is it possible to trigger a textbox text change event after autocompleting using jQuery

I recently implemented a jquery autocomplete plugin for a textbox: $('#TargetArea').autocomplete({ source: '@Url.Action("GetTarget", "Ads", new { type = "Zip", term = target })' }); The implementation is working as expected. ...