Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to set dynamic validation for fields using angular

I'm facing an issue with an existing application, below is my scenario I'm having the below JSON format

.html code

<div class="panel-group" id="accordion">
    <div *ngFor="let property of Tree.properties">
      <div class="panel panel-default">
        <div class="panel-heading">
          <h4 class="panel-title">
            <a class="link" data-toggle="collapse" data-parent="#accordion" href="#dataCatg-{{property.name}}">
              <div *ngIf="property.required">
                <span class="glyphicon glyphicon-chevron-right"></span>{{property.name}}
              </div>
              <div *ngIf="!property.required">
                <span class="glyphicon glyphicon-chevron-right"></span>{{property.name}}
              </div>
            </a>
          </h4>
        </div>
        <div id="dataCatg-{{property.name}}" class="panel-collapse collapse">
          <div class="panel-body">
            <ul class="list-group">
              <li class="list-group-item" *ngFor="let prop of property.details">
                <div *ngIf="prop.details.visible">
                  <div class="row">
                    <div class="col-md-4">
                      <div *ngIf="data.includes(prop.name)">
                        <label class="inline-label" for="{{prop.name}}">{{prop.name}}</label>
                      </div>
                      <div *ngIf="!data.includes(prop.name) ">
                        <label class="inline-label " for="{{prop.name}} ">{{prop.name}}</label>
                      </div>
                    </div>
                  <div class="col-md-8 ">
                      <div *ngIf="!Edit">
                        <span *ngIf="formVisible && metaDataTemplateMap[selectedFile]!==undefined ">
                          <input id="{{prop.name}}" type="{{prop.details.type}} " [(ngModel)]="Data[prop.name]" class="form-control ">
                        </span>
                      </div>
                      <div *ngIf="Edit">
                        <div *ngIf="prop.details.group ">
                          <span *ngIf="formView">
                            <!--need-->
                            <input id="{{prop.name}}" type="{{prop.details.type}}" [(ngModel)]="Edit[prop.name]" (ngModelChange)="Edit($event)" style=" border-radius:0;"
                             class="form-control">
                          </span>
                        </div>
                        <div *ngIf="!prop.details.group ">
                          <input id="{{prop.name}}" type="text " style=" border-radius:0" class="form-control " readonly>
                        </div>
                     </div>
                    </div>
                  </div>
                </div>
              </li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  </div>

.ts Code

Data(res) {


    this.Tree['Properties'] = [];

    for (let property in res.properties) {

      var prop = res.properties[property];
      if (prop['properties'] !== undefined) {
        let temp= {};
        if (res['required'].indexOf(property) !== -1) {
          temp['required'] = true;
        }
        else {
          temp['required'] = false;
        }
        temp['name'] = property;
        let template = {};
        temp['details'] = [];
        for (let nestedProps in prop.properties) {
          let nestedProp = {};
          nestedProp['name'] = nestedProps;

          if (prop.properties[nestedProps]['type'] == 'string' || prop.properties[nestedProps]['type'] == 'date-time') {
            prop.properties[nestedProps]['type'] = 'text';
            template[nestedProps] = '';
          }

          if (prop.properties[nestedProps]['type'] == 'integer') {
            prop.properties[nestedProps]['type'] = 'number';
            template[nestedProps] = 0;
          }

          if (prop.properties[nestedProps]['type'] == 'array') {
            prop.properties[nestedProps]['type'] = 'array';
            template[nestedProps] = '';
          }
          if (prop.properties[nestedProps]['group'] == true) {
            if (this.Edit[property] == undefined)
              this.Edit[property] = {};
            this.Edit[property][nestedProps] = '';
          }

          nestedProp['details'] = prop.properties[nestedProps];

          temp['details'].push(nestedProp);
        }
        this.Data[property] = template;
        this.Tree['Properties'].push(temp);
      }
      if (prop['properties'] == undefined) {
        let temp = {};
        if (res['required'].indexOf(property) !== -1) {
          temp['required'] = true;
        }
        else {
          temp['required'] = false;
        }
        temp['name'] = property;
        if (prop['type'] == 'string' || prop['type'] == 'date-time') {
          prop['type'] = 'text';
          this.Data[property] = '';
        }

        if (prop['type'] == 'number') {
          prop['type'] = 'integer';
          this.Data[property] = 0;
        }

        if (prop['group'] == true) {
          this.Edit[property] = '';
        }
        temp['details'] = prop;
        this.Tree['Others'].push(temp);
      }
    }


  }

here what I'm want is 1. if You see in the JSON I have

"required": [
    "host", 
    "quantity", 
    "id"
], 

while generating the fields it has to check the above mentioned fields are empty or not with out template or reactive form approach how can it be possible if the fields are empty then we have let user know that fields are empty how can I accomplish this ?

like image 505
Madpop Avatar asked Oct 22 '18 03:10

Madpop


People also ask

How do you set a validator dynamically?

We can add Validators dynamically using the SetValidators or SetAsyncValidators. This method is available to FormControl, FormGroup & FormArray. There are many use cases where it is required to add/remove validators dynamically to a FormControl or FormGroup.

Which method is used to add dynamic validation to the forms in angular?

The FormGroup class exposes an API that enables us to set validators dynamically. We need to listen to optionB value changes and based on that we add or remove the validators we require. We also call the control's updateValueAndValidity() method, as we need to recalculate the value and validation status of the control.

What is custom validation in angular?

A validator in Angular is a function which returns null if a control is valid or an error object if it's invalid. For model-driven forms we create custom validation functions and pass them into the FormControl constructor.


1 Answers

Aim

Validation of dynamic generated form input without Reactive or Template Approach.

Solution

Directive will be best use for such kind of requirement. Directive helps to break the complicated job into small independent task. Lets see how it can be implemented.

Implementation provided below doesn't require any change in your existing code.

1. Validate Service

import { Injectable } from '@angular/core';

@Injectable()
export class ValidateService {

  errors = {};

  validate(key: string, value: object) {
    this.errors[key] = value;
  }

  getErrors() {
    let errorList = [];
    Object.keys(this.errors).forEach(key => {
      let value = this.errors[key];
      if ((value == undefined || value == '')  && this.required.find(item=>item == key)) {
        errorList.push({ name: key, error: key + " Field is required" })
      }
    });
    return errorList;
  }

   //All required fields can be maintained here
  required = [
    "host",
    "quantity",
    "id"
  ]

}

2. Validate Directive

ValidateDirective is responsible to collect the current value of input control if any change happens. This information will be passed to the service class ValidationService.

import { Directive, Host, Input, OnChanges, SimpleChanges, ViewContainerRef, AfterViewInit } from '@angular/core';
import { ValidateService } from './validate.service';

@Directive({
  selector: '[validate]'
})
export class ValidateDirective implements OnChanges {

  constructor(private service: ValidateService, private containerRef: ViewContainerRef) {

  }

  @Input("ngModel") model;

  @Input("validate") element;

  ngOnChanges(changes: SimpleChanges) {

    setTimeout(() => {
      this.service.validate(this.containerRef.element.nativeElement.id, changes.model.currentValue);
    })
  }

}

3. Directive Usage

ValidateDirective can be used with any input controls which has id and ngModel.

ex:

<input [validate] id="{{prop.name}}" type="{{prop.details.type}}" [(ngModel)]="Edit[prop.name]" (ngModelChange)="Edit($event)" style=" border-radius:0;" class="form-control">

4. Component ts

ValidateService will be Injected into component to get the list of errors.

constructor(private service:ValidateService) {}

  public get errors(){
    return this.service.getErrors();
  }

5. Displaying errors

Since all errors are available in Component, it can be displayed in the html.

ex :

 <li *ngFor="let error of errors">
     {{error.error}}
 </li>  

Note - There are many thing which can be enhanced further like

  1. Passing custom message to Directive.
  2. Required field list can be passed to Directive as @Input

Working sample demo is here - https://stackblitz.com/edit/angular-xnbzqd

like image 60
Sunil Singh Avatar answered Sep 28 '22 02:09

Sunil Singh