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 ?
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.
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.
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.
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.
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"
  ]
}
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);
    })
  }
}
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">
ValidateService will be Injected into component to get the list of errors.
constructor(private service:ValidateService) {}
  public get errors(){
    return this.service.getErrors();
  }
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
- Passing custom message to
 Directive.Required field listcan be passed to Directive as @Input
Working sample demo is here - https://stackblitz.com/edit/angular-xnbzqd
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With