Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

validation not propagated with custom input component - Angular 4

I have a custom text-area component, with text-area input inside. I have created a custom validator to check the max length (not the html one) of the text. All work fine, the problem is that the inner input is set to invalid (with ng-invalid) while che component itself don't and so also the form that contains the component remains valid. It's seems to work with the built-it required validator, placed on both the component and the input.

How can I make the changes in a custom input to be reflected on the external form?

Thanks! //sorry for my english!

Edit: I made a plunker: https://plnkr.co/edit/NHc25bo8K9OsgcxSWyds?p=preview

This is the custom text-area component html:

<textarea
  [disabled]='disabled'
  [required]='required'
  [placeholder]="placeholder"
  (changes)="onInput($event)"
  (keyup)="onInput($event)"
  [(ngModel)] = "data"
  [name]="name"
  #input="ngModel"
  customMaxLength="{{maxLength}}"
></textarea>
<span *ngIf="input.errors && (input.dirty || input.touched)">
  <span *ngIf="input.errors?.required" class="error-message">Required!</span>
  <span *ngIf="input.errors?.customMaxLength" class="error-message">Max Length Reached({{maxLength}})</span>
</span>

This is the code of the component

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

@Component({
  selector: 'custom-text-area',
  templateUrl: './custom-text-area.component.html',
  styleUrls: ['./custom-text-area.component.less'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TextAreaComponent),
      multi: true
    }
  ]
})
export class TextAreaComponent implements ControlValueAccessor{

  @Input() required = false;
  @Input() name;
  @Input() data;
  @Input() disabled;
  @Input() placeholder = '';
  @Input() errorMsg;
  @Input() maxLength = null;

  @ViewChild('input') input: NgModel;
  constructor() {
    this.data = '';
  }

  propagateChange = (_: any) => {};

  writeValue(value: any): void {
    if (value !== undefined) {
      this.data = value;
    } else {
      this.data = '';
    }
  }

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

  registerOnTouched(fn: any): void {}

  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  onInput(e) {
    this.data = e.target.value || '';
    this.propagateChange(this.data);
  }
}

This is the validator

import { NG_VALIDATORS, Validator, FormControl } from '@angular/forms';
import { Directive, forwardRef, Input } from '@angular/core';

@Directive({
  selector: '[customMaxLength][ngModel]',
  providers: [
    { provide: NG_VALIDATORS, useExisting: forwardRef(() => MaxLengthValidatorDirective), multi: true}
  ]
})
export class MaxLengthValidatorDirective implements Validator{

  @Input() customMaxLength: number;

  ngOnInit(){
  }

  validate(c: FormControl): { [key: string]: any; } {
    if(c.value && this.customMaxLength){
      return c.value.length <  this.customMaxLength ? null : { customMaxLength:{ valid: false } };
    } else {
      return null;
    }
  }

}

Aaand finally this is an use:

<form #form="ngForm">
    <custom-text-area [maxLength]="3" required ngModel name="textArea"></custom-text-area>
</form>
like image 922
Alberto Contorno Avatar asked Mar 17 '26 23:03

Alberto Contorno


1 Answers

The main problem is how you are using the NgModel. You are using it in both the custom component and inside your form. You should only be using it inside of your form. Meaning, textarea should not have an NgModel.

No:

<textarea
  [disabled]='disabled'
  [required]='required'
  [placeholder]="placeholder"
  (changes)="onInput($event)"
  (keyup)="onInput($event)"
  [(ngModel)] = "data"
  [name]="name"
  #input="ngModel"
  customMaxLength="{{maxLength}}"
></textarea>

Yes:

<textarea
  [disabled]='disabled'
  [required]='required'
  [placeholder]="placeholder"
  (changes)="onInput($event)"
  (keyup)="onInput($event)"
  [name]="name"
  customMaxLength="{{maxLength}}"
></textarea>

Here is a working example:

https://plnkr.co/edit/lWZpEpPdnfG7YDiH21jB?p=preview

like image 61
David Anthony Acosta Avatar answered Mar 19 '26 12:03

David Anthony Acosta



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!