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 list
can 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