Search code examples
angularangular-directivecontrolvalueaccessor

How To Use A Directive In A Custom Input With ControlValueAccessor Angular


I've been create a simple custom input component in my angular app use ControlValueAccessor. So, when I want to create a form input element, I don't have to call <input />, only call <my-input>.

I have a problem, when I use <input />, I can use myDirective. For example:

<input type="text" class="form-control" formControlName="name" myDirective />

But, when I use my-input, then I can't use myDirective. For example:

<my-input formControlName="name" myDirective></my-input>

myDirective dosn't work in my-input

This is my-input component use ControlValueAccessor code:

import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

@Component({
  selector: 'my-input',
  templateUrl: './my-input.component.html',
  styleUrls: ['./my-input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(()  => MyInputComponent ),
      multi: true
    }
  ]
})

export class MyInputComponent implements ControlValueAccessor {

  onChange: () => void;
  onTouched: () => void;

  value: string;

  writeValue(value: string): void {
    this.value = value ? value : '';
  }

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

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

Updated: myDirective code:

import { Directive, HostListener } from '@angular/core';
import { FormControlName } from '@angular/forms';

@Directive({
    selector: '[myDirective]'
})

export class MyDirective{
    constructor(private formControlName: FormControlName) { }

    @HostListener('input', ['$event']) 
    onInputChange() {
        this.formControlName.control.setValue(this.formControlName.value.replace(/[^0-9]/g, ''));
    }
}

Is there a way for myDirective to be used in themy-input component?

Thanks in advance.


Solution

  • There're a problem with your directive. Inject a NgControl and control this ngControl

    export class MyDirective{
        constructor(private control: NgControl) { } //<--inject NgControl
    
        @HostListener('input', ['$event']) 
        onInputChange() {
            this.control.control.setValue(this.control.value.replace(/[^0-9]/g, ''));
        }
    }
    

    You can see in stackblitz

    NOTE: Don't forget include in the module declarations

    @NgModule({
      imports:      [ BrowserModule, FormsModule,ReactiveFormsModule ],
      declarations: [ AppComponent, MyInputComponent,MyDirective ],
      bootstrap:    [ AppComponent ]
    })
    export class AppModule { }