How to check for changes in form controls using Angular5

ararb78 picture ararb78 · Dec 7, 2017 · Viewed 13.5k times · Source

Im using reactive forms, I have multiple inputs, I want display a single error for de three inputs, the validatons are; fields are required and blank spaces should not be entered.

component.html:

    <form [formGroup]="contratoForm" class="campo-form">
        <div class="campoFormulario">
            <label class="etiquetaFormulario" for="txtCodigoContrato">Código contrato</label>
            <div style="display:inline-block; position: relative;"  class="control">
                <input [formControl]="txtCodigoContrato" type="text" id="txtCodigoContrato" name="txtCodigoContrato" class="cajaTexto ancho80" />
                <span>/</span>
            </div>                
            <div style="display:inline-block; position: relative;" class="control">                
                <input [formControl]="txtCodigoContrato2" type="text" id="txtCodigoContrato2" name="txtCodigoContrato2" class="cajaTexto ancho80" />              
            </div>
        </div>
        <div class="campoFormulario">
            <label class="etiquetaFormulario" for="txtContrato">Contrato</label>
            <div style="display:inline-block; position: relative;" class="control">
                <input [formControl]="txtContrato" type="text" id="txtContrato" name="txtContrato" class="cajaTexto ancho350" />

            </div>
        </div>
         <div *ngIf="((!txtCodigoContrato.valid && (txtCodigoContrato.dirty || txtCodigoContrato.touched)) && (txtCodigoContrato.hasError('required') || txtCodigoContrato.hasError('hasWhites')))
                ||
                ((!txtCodigoContrato2.valid && (txtCodigoContrato2.dirty || txtCodigoContrato2.touched)) && (txtCodigoContrato2.hasError('required') || txtCodigoContrato2.hasError('hasWhites')))
                ||
         ((!txtContrato.valid && (txtContrato.dirty || txtContrato.touched)) && (txtContrato.hasError('required') || txtContrato.hasError('hasWhites')))
                ">

            <span>check the fields in red, they are required</span>    
        </div>              
        <div class="buttons">
            <button class="btn btn-primary boton" type="button" style="float:right;" (click)="onCloseLink()">
                <span class="glyphicon glyphicon-off"></span> Cancel
            </button>

            <button class="btn btn-primary boton" type="button" style="float:right;" (click)="limpiar()">
                <span class="glyphicon glyphicon-trash"></span> Clean
            </button>
            <button type="submit" [disabled]="!contratoForm.valid" class="btn btn-primary boton" style="float:right;" (click)="onClick()">
                <span class="glyphicon glyphicon-floppy-disk"></span> Save
            </button>
        </div>
    </form>

component.ts:

    import { Component, HostBinding, OnDestroy, OnInit, Input, Output, EventEmitter, ElementRef, NgModule, ViewChild, ChangeDetectorRef  } from '@angular/core';
    import { Validators, FormBuilder, FormControl, FormGroup, AbstractControl} from '@angular/forms';
    import { AfterViewChecked } from '@angular/core/src/metadata/lifecycle_hooks';

    @Component({
        selector: 'app-formulario-contrato',
        templateUrl: './contrato.component.html',
        styleUrls: ['./contrato.component.css'] 
    })
    export class FormularioContratoComponent  {

        contratoForm: FormGroup;  

        constructor(private elRef: ElementRef, private formBuilder: FormBuilder, private cdRef: ChangeDetectorRef) {     
            this.createForm();           
        }

        txtCodigoContrato = new FormControl('', [
            Validators.required
            ,this.hasNotWhites
        ]);
        txtCodigoContrato2 = new FormControl('', [
            Validators.required
            , this.hasNotWhites
        ]);
        txtContrato = new FormControl('', [
            Validators.required
            , this.hasNotWhites
        ]);

        createForm() {
            this.contratoForm = this.formBuilder.group({
                txtCodigoContrato: this.txtCodigoContrato
                ,txtCodigoContrato2: this.txtCodigoContrato2
                ,txtContrato: this.txtContrato           
            });
        }  

        hasNotWhites(fieldControl: FormControl) {
            if (fieldControl.value.trim() != '') {
                return null
            }
            else {
               return { hasWhites: true };
            }          
        }

        ngAfterViewChecked() {
            this.cdRef.detectChanges();
        }

        limpiar() {   
        }

        onClick() {
            return null;
        }

component.css:

/* some stuff...*/
:host /deep/ .control input.ng-invalid.ng-touched {
  border-color: #ff8080;
}

The validations work correctly, that is, they jump when the field stays empty or when I enter blank spaces. My problem is that I have to add more form controls and the if it contains the message can be made illegible:

  <div *ngIf="((!txtCodigoContrato.valid && (txtCodigoContrato.dirty || 
  txtCodigoContrato.touched)) && (txtCodigoContrato.hasError('required') || 
  txtCodigoContrato.hasError('hasWhites')))
                ||
                ((!txtCodigoContrato2.valid && (txtCodigoContrato2.dirty || 
  txtCodigoContrato2.touched)) && (txtCodigoContrato2.hasError('required') || 
  txtCodigoContrato2.hasError('hasWhites')))
                ||
         ((!txtContrato.valid && (txtContrato.dirty || txtContrato.touched)) 
  && (txtContrato.hasError('required') || txtContrato.hasError('hasWhites')))
                ">

Is there any way to control these values by typescript, that is, each time a value in the control changes it is controlled by a function in typeScript that returns true or false and if only ask for the value of that function in my div?

Answer

Eduardo Vargas picture Eduardo Vargas · Dec 7, 2017

You can do something like

this.contratoForm.valueChanges.subscribe(value = > //do something)

If you want you can add a map function before to get just the values from the form you care about that are changing

this.contratoForm.valueChanges
.map(values => values.txtCodigoContrato)
.subscribe(txtCodigoContrato = > //do something)