Search code examples
angulartypescriptangular-materialangular6angular-forms

Warning messages are displaying even after the data present in the input field


I have a component used to display some data (i,e first name, last name, phone ....etc) from the api. In order to perform CRUD operation specially update operation,.
As in below image:

enter image description here

I am facing an issue, when I click SAVE button even the data present in the input field(i,e phone). It's is still showing the warning messages(i,e mat-error). As in below image:

enter image description here

Below is my component code

HTML

<form [formGroup]="editForm">

      <div>
        <mat-form-field>
          <input matInput placeholder="First Name" formControlName="firstname" required>
          <mat-error *ngIf="editForm.controls.firstname.hasError('required')">
            Please enter first name
          </mat-error>
        </mat-form-field>
      </div>

      <div>
        <mat-form-field class="example-full-width">
          <input matInput  placeholder="Last Name" formControlName="lastname" required>
          <mat-error *ngIf="editForm.controls.lastname.hasError('required')">
            Please enter last name
          </mat-error>
        </mat-form-field>
      </div>

      <div>
        <mat-form-field class="phone-number">
          <input matInput placeholder="Phone Number" formControlName="phonenumber" required>
          <mat-error *ngIf="editForm.controls.phonenumber.hasError('required')">
            Please enter phone number
          </mat-error>
          <mat-error *ngIf="editForm.controls.phonenumber.hasError('pattern')">
            Please enter a valid phone number
          </mat-error>
        </mat-form-field>
      </div>

      <div class="btn-sec">
        <button mat-flat-button  type="button" >Cancel</button>
        <button mat-flat-button  type="submit" (click)="onEditForm()">Save</button>
      </div>

   <form>

TS

import{ Component, Inject, Input, OnInit, ViewChild } from '@angular/core';
import{ FormBuilder, FormControl ,FormGroup, Validators}fro'@angular/forms';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {IContact } from 'src/app/models/app.models';


@Component({
  selector: 'wsd-update-customer',
  templateUrl: './wsd-customer.component.html',
  styleUrls: ['./wsd-customer.component.css'],
})

export class EditCustomerComponent implements OnInit {

 public editForm: FormGroup;

constructor(@Inject(MAT_DIALOG_DATA) public data: IContact,
          private fb: FormBuilder,
          public dialog: MatDialog) {} 

public ngOnInit(): void {
  this.editForm = this.fb.group({
    firstname: [ null, [Validators.required],
    lastname: [null, [Validators.required],
    phonenumber: [null, [Validators.required, Validators.pattern('[0-9]+')]],
   });

this.editForm.get('firstname').setValue(this.data.firstName);
this.editForm.get('lastname').setValue(this.data.lastName);
this.editForm.get('phonenumber').setValue(this.data.phoneNumbers[0].number);
}

 public onEditForm(): void {
   this.markAsDirty(this.editForm);
 }


 private markAsDirty(group: FormGroup): void {
    group.markAsDirty();
     for (const i in group.controls) {
      group.controls[i].markAsDirty();
   }
  }

}

models.ts file

export interface IContact {
  firstName:  string;
  lastName:   string;
   phoneNumbers:  IPhoneNumber[];
 }

 export interface IPhoneNumber {
  type:        string;
  number:      string;
 }

JSON

 {
    "firstName": "Adaline",
   "lastName": "Danat",
   "phoneNumbers": [
      {
        "type": "Home",
        "number": "+62 342 886 8201"
      },
      {
        "type": "Business",
        "number": "+63 704 441 1937"
      },
      {
        "type": "Unknown",
        "number": "+63 530 693 2767"
      }
   ],

}

Updated Photo

enter image description here

Updated Stckblitz link


Solution

  • You can do with the combination of FormGroup and FormControl where FormGroup accept an object of AbstractControl Class.

    So if you are using FormGroup then it accepts the parameter called controlsConfig which describes:

    @param controlsConfig A collection of child controls. The key for each child is the name under which it is registered

    So just define the FormGroup with FormControls and add the validation and default value as to the particular Control:

    this.editForm = this.fb.group({
          firstname: new FormControl([null, [Validators.required]]),
          lastname: new FormControl([null, [Validators.required]]),
          phonenumber: new FormControl([null, [Validators.required, Validators.pattern('[0-9]+')]]),
    });
    

    EDIT:

    To assign the value of formControl in a p, span that is except input type, there are three ways to do this

    1) With using Two-way data binding on data itself like:

    <p>{{data.email}}</p>
    

    2) By using FormControl:

    <p>{{editForm.value.email}}</p>
    

    but for this, you have to define a FormControl in the TS file and use setValue to assign the value.

    email: new FormControl([null])  // define a control in the group
    
    this.editForm.get('email').setValue(this.data.email); // set value from data object
    

    3) You use the FormContol with readonly attribute

    <mat-form-field class="example-full-width">
         <input matInput  placeholder="Email" formControlName="email" readonly>
    </mat-form-field>
    

    Working StackBlitz Example