Madpop
Madpop

Reputation: 725

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 ?

Upvotes: 8

Views: 1255

Answers (1)

Sunil
Sunil

Reputation: 11243

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

Upvotes: 5

Related Questions