Angular: updateValueAndValidity from directive

随声附和 提交于 2021-01-02 06:40:26

问题


I have a directive that appends decimals, if the input value is a whole number, on blur. Below is the implementation.

import { Directive, ElementRef, Input, OnInit, HostListener, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Directive({
  selector: '[price]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => PriceDirective),
      multi: true
    }
  ]
})
export class PriceDirective implements ControlValueAccessor {

  constructor(private el: ElementRef) { }

  // ControlValueAccessor interface
  private _onChange = (_) => { };
  private _onTouched = () => { };

  @HostListener('blur', ['$event'])
  input(event) {
    !!event.target.value ? $(this.el.nativeElement).val(Number(event.target.value).toFixed(2)) : $(this.el.nativeElement).val(null);

    this._onChange(parseFloat(event.target.value));
    this._onTouched();
  }
  writeValue(value: any): void {
    !!value ? $(this.el.nativeElement).val(Number(value).toFixed(2)) : $(this.el.nativeElement).val(null);
  }

  registerOnChange(fn: (_: any) => void): void { this._onChange = fn; }
  registerOnTouched(fn: any): void { this._onTouched = fn; }

}

Things work as expected.

But, since Angular doesn't trigger validation when the value is changed programatically, the textbox that has this directive, is not validated.

How can I enable validation in this case, by other means than passing the control reference as an input to the directive and calling updateValueAndValidity on it, or calling updateValueAndValidity on input or blur.

It would be great if someone suggests me a way to trigger validation from the directive itself.


回答1:


I solved the same problem in this way. It is my first approach.

  update() {

    // ...

    const el = this.el.nativeElement;
    const reg = new RegExp(this.textMaskConfig.replacement);

    el.value = this.prevStr.replace(reg, this.currentChar);

    // ...

  }

But It doesn't fire the validate event. So I get the NgControl component and used setValue() method.

  constructor(private el: ElementRef, private ctrl: NgControl) {   
  }

  @HostListener('keydown', ['$event']) onKeyDownHandler(e) {
    this.ctrl.control.setValue(value);
  }



回答2:


I solved what I believe is the same problem. I couldn't get validation to fire when I set the value on the nativeElement like in your example, or with a HostBinding to value like @HostBinding('value') public value: string;. But I could get validation to fire when I set the value through ngModelChange like this:

import { Directive, Input, HostListener, Output, EventEmitter } from '@angular/core';

@Directive({
    selector: '[appPrice]',
})
export class PriceDirective {
    @Output()
    public ngModelChange: EventEmitter<any> = new EventEmitter();

    @HostListener('blur', ['$event.target.value'])
    public formatANumber(value) {
        const cleanedValue = Number(value).toFixed(2);
        this.ngModelChange.emit(cleanedValue);
    }
}



回答3:


it is not clear to me what validation you want to add. As far as I understood, you want to reach input element of form from a directive and manipulate it based on some logic. I ll show you one way with rx.js and based on your validation logic, you can use corresponding operator.

in your generated directive file:

import { Directive, ElementRef } from '@angular/core';
import { NgControl } from '@angular/forms';
import { map } from 'rxjs/operators';

@Directive({
  selector: '[appPrice]',
})

export class PriceDirective {
  // dependency injection for ElementRef has to set in the constructor
  constructor(private el: ElementRef, private controlName: NgControl) {
    console.log(this.el);
    console.log('controlName', controlName);
    // this returns FormControlName (not FormControl) obj. it has name property that tells u which formCpntrol element u r on.
    // FormContolName class binds the FormControl to the "input" element. FormControlName has no direct reference to FormGroup
    // controlName is bound to input element's formGroup's FormGroup
  }
  ngOnInit() {
    console.log(this.controlName.control);
    // this returns the name of the formControl
    console.log(this.controlName.control.parent);
    // this.controlName.control.parent takes us to the FormGroup
    // this.controlName.control.parent returns observable. valueChanges watches all the formControls that you defined. if you have "a","b","c" formControls
    // with pipe() you can add operators to modify the value
    this.controlName.control.parent.valueChanges
      .pipe(map(({ a, b, c }) => // add Some Logic here))
      .subscribe((value) => {
        if (here is True) {
          this.el.nativeElement.classList.add('close');
        } else {
          this.el.nativeElement.classList.remove('close');
        }
      });
  }
}


来源:https://stackoverflow.com/questions/48472868/angular-updatevalueandvalidity-from-directive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!