Issues with ControlValueAccessor causing problems with functionality of Reactive Forms

In my coding project, I have developed a custom control named TextBox. In this post, I will only showcase the relevant snippets of code related to this control.

    @Component({
      selector: 'app-textbox',
      template:
      `
        <input [(ngModel)]="value" [disabled]="disabled" />
      `,
      styleUrls: ['./textbox.component.css']
    })
    export class TextboxComponent implements OnInit, ControlValueAccessor {
    
      constructor() { }
      writeValue(obj: any): void {
        this._value = obj;
      }
      registerOnChange(fn: any): void {
        this.onChange = fn;
      }
      registerOnTouched(fn: any): void {
        this.onTouch = fn;
      }
      setDisabledState?(isDisabled: boolean): void {
        this.disabled = isDisabled;
      }
    
      disabled = false;
    
      onChange:()=>{}
      onTouch:()=>{};
    
      private _value:string;
      public get value():string {
        return this._value
      } 
      public set value(value:string){
        this._value = value;
      }
    
      ngOnInit(): void {
      }

Additionally, the structure of my app.component.ts file is as follows:

    @Component({
      selector: 'app-root',
      template:
      `
        <form [formGroup]="form" novalidate>
          <div>
            <label >Name</label>
            <app-textbox formControlName="name"></app-textbox>
          </div>
        </form>
      `,
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit{
      /**
       *
       */
      constructor(private formBuilder:FormBuilder) {
      }
    
      form = this.formBuilder.group({
        name:['', Validators.required]
      })
    
      model:NameModel = {
        name:'test'
      }
    
      ngOnInit(): void {
        this.form.get('name').setValue(this.model.name);
      }
    }
    
    interface NameModel{
      name:string;
    }

Upon running the application, I anticipated that the textbox would display the text "test." However, this was not the case. Can someone provide insight into why this may be happening?

It's worth noting that when I execute this.form.get('name')?.value, the correct value is returned.

Answer №1

To ensure proper functionality, consider moving the line

this.form.get('name').setValue(this.model.name);
to the ngAfterViewInit lifecycle hook instead of ngOnInit. Alternatively, you can call
this.form.updateValueAndValidity();
after setting the value.

Two important notes to keep in mind:

  1. Don't forget to update the form-control value after it changes in your TextboxComponent. You should invoke the registered onChange method within the value setter as shown below:
private _value: string;
public get value(): string {
  return this._value;
}
public set value(value: string) {
  this._value = value;
  this.onChange(value);
}
  1. In your scenario, it's recommended to initialize the FormGroup in the ngOnInit method and set the default value of name directly like this:
form: FormGroup;

ngOnInit(): void {
  this.form = this.formBuilder.group({
    name: [this.model.name, Validators.required],
  });
}

Answer №2

When using the writeValue(obj) function, make sure to assign the input received as obj to this._value.

writeValue(obj: any): void {
  if (obj !== this._value) {
    this._value = obj;
  }
}

Also, remember to include providers for NG_VALUE_ACCESSOR and use multi-provider to extend existing providers. (Check out References 1)

@Component({
  selector: 'app-textbox',
  template: `
    <input [(ngModel)]="value" [disabled]="disabled" />
  `,
  styleUrls: ['./textbox.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TextboxComponent),
      multi: true
    }
  ]
})
export class TextboxComponent implements OnInit, ControlValueAccessor {

  writeValue(obj: any): void {
    if (obj !== this.value) {
      this._value = obj;
    }
  }

  ...
}

View Sample Solution on StackBlitz


References

Learn how to link your custom control to ngModel using Control Value Accessor.

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

Intercepting HTTP requests in Angular, but not making any changes to the

Working on Angular 13, I am trying to attach a JWT token to the headers in order to access a restricted route on the backend. However, after inspecting the backend, it seems that the JwtInterceptor is not modifying the HTTP request headers. I have included ...

Error: Missing provider for InjectionToken DEFAULT_LOCALE

I have been exploring the setup of an Angular 2 project with i18n. I followed a tutorial here that uses Transloco, and everything seemed to work perfectly. However, when running the unit tests, I encountered an error that I couldn't find any informati ...

execute the node application using the .desktop file

Hello, I am attempting to launch an application in Linux by double-clicking it. I have come across the .desktop file as a solution (I need this method because the app will be deployed on a Raspberry Pi and users prefer not to use the terminal). Here is wha ...

The upcoming developer manages to execute the program successfully, however, it continues to load indefinitely

Executing the command yarn dev consistently runs successfully in my VS Code terminal: $ yarn dev yarn run v1.22.19 warning ..\..\..\..\package.json: No license field $ next dev ready - started server on 0.0.0.0:3000, url: http://localho ...

Issue with Promise not resolving in Node when using Edge

As I explore the best way to utilize my C# dlls with Edgejs for Node, I encountered a situation where one proxy function in Node appears like this (a class method in Typescript): readSettings(args: ReadSettingsParams) : Promise<response> { let $ ...

Is it possible to parse a concise year format using date-fns?

Is there a way to utilize the Date-Fns Adapter for Angular Material in order to parse short date formats that do not have an explicit separator? For instance, 010123 should be converted to 01.01.2023. It works fine with the full year: 01012023 is correctly ...

Is it possible to utilize the HttpXsrfInterceptor and HttpXsrfCookieExtractor classes for CSRF configuration in Angular 16, despite Intelli-J indicating that they do not exist?

In a valuable article about configuring CSRF for Angular, two options are outlined: First: opt for the default Csrf configuration: providers: [ { provide: HTTP_INTERCEPTORS, useExisting: **HttpXsrfInterceptor**, multi: true } ] Second: If you're usi ...

Using RxJS switchMap in combination with toArray allows for seamless transformation

I'm encountering an issue with rxjs. I have a function that is supposed to: Take a list of group IDs, such as: of(['1', '2']) Fetch the list of chats for each ID Return a merged list of chats However, when it reaches the toArray ...

Having trouble with @HostListener on iPad or iOS devices? I'm currently using a Bluetooth keyboard to navigate and interact with an Angular app

I am currently creating a web application using Angular 6 for an iPad with a screen size of 9.7 inches. I have implemented code similar to the one found at this link. import { Component, HostListener } from '@angular/core'; export enum KEY_CODE ...

Unable to trigger an alert within a function in Ionic 4

I need to display an alert within a function that is using Firebase authentication. This is my TypeScript code (with jQuery enabled): async showEmptyAlert() { const empty = await this.alertController.create({ header: 'Error!', m ...

The error message "NodeJS-Typescript-Yarn : Error: Module 'd3' not found" is displayed

I encountered an issue with importing d3 in my project. I am using [email protected] and Yarn. The problem arises when the file Layout.ts (Layout.js) tries to import d3, resulting in an error. import * as D3 from "d3"; The error persists ev ...

What is the best way to utilize switchMap in order to terminate ongoing http requests and only receive the latest subscription?

To cancel a pending HTTP request, I attempted using `subscription.unsubscribe` in this manner: getAgentList(pageNumber: number, filter: string): any { let requestUrl: string = 'api/service/agents_search?ACCT=' +this.accountId; if ( this. ...

Tips for creating case-insensitive query parameter values

Can someone help me troubleshoot why my endpoint for a GET method to /book with a query parameter named name is not returning the correct book title? When the name parameter is 'scott' or 'SCOTT,' it should return "Cracking the Coding I ...

Service that spans the entire application without relying on a service that is also used throughout the application

In continuation of my previous question, I am facing an issue with the No provider for ObservableDataService. I have an application-wide service named UploadedTemplatesService which must be a singleton. This service has one dependency - ObservableDataServ ...

Include a <button> element in the Angular ng2-pdf-viewer framework

I am looking to showcase a PDF file on my component using ng2-pdf-viewer. One of the requirements is to include a download button that overlaps the PDF file. I have searched for references on how to achieve this but unfortunately, I haven't found any ...

Setting the value of a form control within a form array in an Angular reactive form can be achieved by following these steps

I have a reactive form with multiple entity entries: this.entityDetailsForm = new FormGroup({ entitiesArray: new FormArray([ new FormGroup({ id: new FormControl(), name: new FormControl(), startDate: new Form ...

What is the best way to retrieve the 5 most recent posts for each genre using Laravel's API

In the process of developing an application (which is my academic project), I am utilizing Laravel 5.4 as my API combined with Angular 5. The focus of my project revolves around a music blog, necessitating specific features like categories and subcategorie ...

Having trouble accessing a variable from the material theme in Angular 7

Currently, I am working with Angular 7.0.3 and endeavoring to establish an scss variable with the primary color of my material theme. // src/styles/_variables.scss @import "~@angular/material/theming"; @include mat-core(); $app-primary: mat-palette($mat-i ...

Create an eye-catching hexagon shape in CSS/SCSS with rounded corners, a transparent backdrop, and a

I've been working on recreating a design using HTML, CSS/SCSS in Angular. The design can be viewed here: NFT Landing Page Design Here is a snippet of the code I have implemented so far (Typescript, SCSS, HTML): [Code here] [CSS styles here] [H ...

Discuss the communication paths between Server and Client components in the upcoming 14 days

Currently, my objective is to transfer state from a client component to a server component, perform some actions on the server, and then send the updated state back to the client through props. I am in the process of building a booking system using tools ...