I have html that is dynamically generated from JSON and I would like to implement some validation logic.
To clarify, here is an example:
<div ng-switch on="field.type" ng-hide="{{ field.hide }}">
<div ng-switch-when="input" class= "col-md-6" ng-class="{'has-error': reqField(field.name, entity[field.name]) }">
<input
ng-model="entity[field.name]" id="{{field.name}}" class="form-control"
type="text" ng-style="setStyle(field.style)" ng-change="{{field.change}}" />
</div>
</div>
{
"title": "Json Title",
"id": "j_id",
"groupfields": [
{
"name": "jsonname",
"title": "JSON Title",
"type": "jsonselect",
"namevalue": "jsonvalue",
"combo": "jsondropdown",
"change" : "jsonChanged()"
},
{
"name": "jsonEmail",
"title": "JSON Email Address",
"type": "display"
},
{
"name": "jsonPhone",
"title": "JSON Phone Number",
"type": "display"
}
]
}, {
"title": "Json Title",
"id": "j_id",
"groupfields": [
{
"name": "jsonname",
"title": "JSON Title",
"type": "jsonselect",
"namevalue": "jsonvalue",
"combo": "jsondropdown",
"change" : "jsonChanged()"
},
{
"name": "jsonEmail",
"title": "JSON Email Address",
"type": "display"
},
{
"name": "jsonPhone",
"title": "JSON Phone Number",
"type": "display"
}
]
},
if (entityName) {
return false;
} else {
return true;
}
So for clarification, ex: 'field.type' in the ng-switch on is the "type" in the JSON - and we can determine which html div to display the content based on different JSON keys/values.
This implies that one html div could potentially be used to generate hundreds of input fields so this needs to be dynamic.
I would like to add validation for when a required field is empty. At the moment, I've tried adding the ng-class="{has-error': } which points to my function reqField. However, because this function is getting fired for EVERY field with "type": jsonselect, this function is checking whether or not the field is empty - which is really inefficient in terms of speed and usability (super laggy).
The javascript you see above is more or less the logic that the function reqField() does in order to check whether or not the field is empty (very inefficient since we're checking hundreds).
What I would like to use is something along the lines of ng-required={{field.required}} and make a new key/value in the JSON to determine whether or not I want this field to be a required field (sort like this):
{
"title": "Json Title",
"id": "j_id",
"groupfields": [
{
"name": "jsonname",
"title": "JSON Title",
"type": "jsonselect",
"namevalue": "jsonvalue",
"combo": "jsondropdown",
"change" : "jsonChanged()"
},
{
"name": "jsonEmail",
"title": "JSON Email Address",
"type": "display",
"required": true
},
{
"name": "jsonPhone",
"title": "JSON Phone Number",
"type": "display",
"required": true
}
]
},
~ and somehow pass that information to the ng-class="{has-error': } so that we can highlight - or do whatever we want once we know the field has been filled in/or is empty.
Form validation is built in to AngularJS. The ng-required directive will add error state to the form object, so that you can set up your ng-class like so:
<div ng-switch-when="input" class= "col-md-6" ng-class="{'has-error': myForm[field.name].$error.required }">
Your form and input must have name attributes for this to work:
<form name="myForm">
...
<input name="{{field.name}}" ng-required="field.required">
Related
I have an Alpaca JS form comprised of an array of items which each consist of a textbox and a checkbox. For some reason, when I change the order using the dynamic controls, it successfully renumbers the textbox but doesn't change the number of the checkbox. This also results in a duplicate name assigned if the same top button to dynamically add new fields is pressed. The end result is incorrect data being passed when the form is submitted. How can I fix this to properly renumber the checkboxes?
Here's a sample of the Alpaca configuration:
$("#form1").alpaca({
"schema": {
"title": "Testing checkbox array IDs",
"description": "Testbox checkbox array test.",
"type": "object",
"properties": {
"form-fields": {
"title": "Fields",
"description": "These are the fields.",
"type": "array",
"items": {
"type": "object",
"properties": {
"field-name": {
"type": "string",
"title": "Field Name",
"description": "Enter the name for this field.",
"required": true
},
"field-box": {
"type": "boolean",
"title": "Field Box",
"description": "Check this box.",
"default": false
}
}
}
}
}
}
});
I couldn't find a way to correct the behavior itself but I was able to work around it by adding a postRender event to the Alpaca definition as follows:
"postRender": function(control) {
control.childrenByPropertyId["form-fields"].on("move", function() { $('input[type=checkbox]').each(function(index) { $(this).attr("name", $(this).closest("div:has(*[name])").first().attr("name")) }); });
control.childrenByPropertyId["form-fields"].on("add", function() { $('input[type=checkbox]').each(function(index) { $(this).attr("name", $(this).closest("div:has(*[name])").first().attr("name")) }); });
control.childrenByPropertyId["form-fields"].on("remove", function() { $('input[type=checkbox]').each(function(index) { $(this).attr("name", $(this).closest("div:has(*[name])").first().attr("name")) }); });
}
This is a bit of a hack but it works because the parent object does get assigned the correct name value and the form will post with those values if the name is just copied down into the input elements.
I'm working on creating my own schematics. This schematics will be responsible for creating a component (container) with some code. Template of this component will contain a few other components. One of this component will be banner component that will be optional. This banner will display text that will be translated into other languages, so I also should ask the user to provide (default) translation text if the banner will be included in the component.
Here is an example of this template:
name#dasherize.component.html.template:
<% if (includeBanner) { %>
<app-banner [title]="'<%= translationModuleKey %>.banner.title' | translate"
[description]="'<%= translationModuleKey %>.banner.description' | translate">
</app-banner>
<% } %>
<app-other-component>
</app-other-component>
Here is my schema.json:
{
"$schema": "http://json-schema.org/schema",
"id": "MySchematics",
"title": "My schematics",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the container",
"x-prompt": "Container name"
},
"includeBanner": {
"type": "boolean",
"description": "Include banner",
"default": "true",
"x-prompt": "Include banner"
},
"bannerTitle": {
"type": "string",
"description": "Banner title",
"x-prompt": "Banner title"
},
"bannerDescription": {
"type": "string",
"description": "Banner description",
"x-prompt": "Banner description"
},
"translationModuleKey": {
"type": "string",
"description": "Root key for translations"
}
},
"required": [
"name", "includeBanner", "bannerTitle", "bannerDescription"
]
}
My problem is that when user will set includeBanner to true, fields bannerTitle and bannerDescription should be required and there should be displayed prompt if those properties were not provided, but if includeBanner will be false, bannerTitle and bannerDescription shouldn't be required and there shouldn't be displayed prompt to fill these properties if those properties were not provided.
Any idea how to achieve that?
I was struggling with the same problem. What I've discovered - if you need conditional prompts, then you can't rely on declarative schema.json file (it doesn't support conditions).
Instead, you should use the askConfirmation function from #angular/cli/utilities/prompt.
So your example could look like this:
import { askConfirmation } from '#angular/cli/utilities/prompt';
export function yourSchematicsFn(options: Schema): Rule {
return async (tree: Tree, context: SchematicContext) => {
const includeBanner = await askConfirmation('Include Banner?', true);
if(includeBanner) {
// ask for bannerTitle and bannerDescription
}
else {
// do something else
}
return chain(/* chain your rules here */);
}
}
I've discovered this in Angular CLI ng-add schematics source code: askConfirmation.
I am making angular application and building of angular dynamic form.
Here i am trying to split of form in two parts such as Person Name and Personal details..
For Person Name its working for grouping but for Personal details its not working.
The Html:
<div *ngIf="form">
<div *ngFor="let question of questions" class="form-row" [formGroup]="form">
<ng-container *ngIf="!question.children">
<app-question [question]="question" [form]="form"></app-question>
</ng-container>
<ng-container *ngIf="question.controlType === 'group' && question.children && question.children.length > 0">
<app-dynamic-group [questions]="question.children" [form]="form.controls[question.key]" [key]="question.key" [formControlName]="question.key"></app-dynamic-group>
</ng-container>
</div>
</div>
JSON:
jsonData: any = [
{
"elementType": "group",
"key": "person_name",
"children": [
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "first_name",
"label": "First Name",
"type": "text",
"value": "",
"required": true,
"minlength": 3,
"maxlength": 20,
"order": 1
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "last_name",
"label": "Last Name",
"type": "text",
"value": "",
"required": true,
"order": 2
}
],
},
{
"elementType": "group",
"key": "personal_details",
"children": [
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "email",
"label": "Email",
"type": "text",
"value": "",
"required": true,
"minlength": 3,
"maxlength": 20,
"order": 1
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "mobile",
"label": "Mobile",
"type": "text",
"value": "",
"required": true,
"order": 2
}
],
},
];
The working Stckblitz: https://stackblitz.com/edit/angular-x4a5b6-5uj52y
As of working everything works fine.. Already a group was made for Person name and its working fine but for Personal details i am unable to find the input boxes..
A single form needs to get split up with titles above each part thats the requirement of this form.
Here the {{question.key}} displays the name on each input boxes but i need to display only Person Name at top.. Because it is the parent title and the remaining such as First Name, Last Name are input box labels.. How to show the parent title alone in before of each part (Person Name (Has First and Last Name) , Personal Details (Has Email and Mobile))...
I would like to have order split up exactly like the below with title for each respectively.
Person Name
-> First Name
-> Last Name
Personal Details
-> Email
-> Mobile Number
If i am wrong with the above approach then kindly help me to split this https://stackblitz.com/edit/angular-x4a5b6-geesde dynamic form like the below given approach..
My form needs to look like this https://stackblitz.com/edit/angular-zc34qr but it needs to be in pure angular dynamic form and JSON loading..
Kindly help me to create a group Personal Details like the Person Name which was already created and working..
Stuck for a long duration in this kindly help me please...
I don't understand why you creates additional formGroup here:
this.form = new FormGroup({main: innerForm});
Just use formGroup you're getting from your service:
dynamic-form.component.ts
this.form = this.qcs.toFormGroup(this.questions);
dynamic-form.component.html
<app-dynamic-group [questions]="questions" [form]="form"></app-dynamic-group>
Now, you do not need to implement ControlValueAccessor on your DynamicGroupComponent. You're passing FormGroup to it and it should be enough to generate form dynamically.
dynamic-group.component.ts
#Component({
selector: 'app-dynamic-group',
templateUrl: './dynamic-group.component.html'
})
export class DynamicGroupComponent {
#Input() form: FormGroup;
#Input() questions: QuestionBase<any>[] = [];
}
dynamic-group.component.html
<div *ngFor="let question of questions" class="form-row">
<app-question *ngIf="!question.children" [question]="question" [form]="form"></app-question>
<app-dynamic-group
*ngIf="question.controlType === 'group' && question.children && question.children.length"
[form]="form.get(question.key)"
[questions]="question.children">
</app-dynamic-group>
</div>
Forked Stackblitz
I am building a website to help operator of my team to generate a data source config.
Since there are many options need to be handled. I design an common solution to render the page: parse a config file(maybe, xml or json format) to html with some rules. Maybe something like below:
{
"data_type": {
"title": "Data type",
"description": "Select what type do you want.",
"fields": [{
"title": "promotion",
"description": "data from promotion center"
"type": "redio",
"default": true,
"value": "p",
"name": "dtype"
}, {
"title": "brands",
"description": "data from brands center"
"type": "redio",
"default": false,
"value": "b",
"name": "dtype"
}]
}
}
then it can be parsed as:
<div class="form-control-group">
<h3>Data type</h3>
<p>Select what type do you want.</p>
<div class="form-control-filed">
<input type="redio" name="dtype" value="p" show="dtype" checked />
<p>data from promotion center</p>
</div>
<div class="form-control-filed">
<input type="redio" name="dtype" value="b" />
<p>data from brands center</p>
</div>
</div>
This step is easily to implement. But since there are some cases like. When I click redio A, I wanna hide the checkbox B. After I unchecked Checkbox C, I wanna show up the input D. I have no idea about how to design the config file to describe the logic about different fields.
So, the key point is, I wanna parse the html by a json snippet. Then maybe there are some symbols to mark the behavior of each form field, I can use my common js to bind events to handle the show/hide, focus/blur or something else logic. Like the attribute show, I can use my common js function to detect it and bind click event to that radio button, and show up the element with name="dtype" after this radio is clicked.
I am not sure if it is a good solution and how to design an reasonable json structure.
Hope any one can provide some suggestions. Thanks in advance.
I've given up for this case. For my requirement, I use a json file to config the information of the form field with a behavior definition to describe show/hide functionality.
{
"xxx": {
"title": "Price mode",
"description": "group_description",
"fields": [{
"title": "",
"type": "select",
"name": "price_mode",
"value": [{
"text": "Store with product",
"value": 1,
"default": true,
"behaviors": {
"show": "name1|name2|name3",
"hide": ""
}
}, {
"text": "Product",
"value": 2,
"default": false,
"behaviors": {
"show": "",
"hide": "name1|name2|name3"
}
}],
"require": true,
"rule": ""
}]
}
}
Codes of server side will parse this config and render as html snippet with special attribute storing behavior definition, then use javascript functions handle these attributes and care about the behavior after doing something like click the radio button or change the value of select list.
Not sure if the question makes sense so better to provide a code example.
{
"data": {
"whatever": [{
"id": "abcd12312",
"title": null,
"value": null,
"options": [
{
"text": "My text {{value}} one",
"value": "email#address.com"
},
{
"text": "My text [value] one",
"value": "email#address.com"
}
]
}]
}
}
So I was thinking can you do anything with Mustache or is there another way without having to write JavaScript to insert the value inside the text?
What I am trying to do is to allow the user to change the text to what ever they want but have a pointer to the value which will be dynamic based on what email address the entered on a previous page.