I am making form builder using angular dynamic form, where i am loading the data for form from JSON as,
jsonData: any = [
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "first_name",
"label": "First Name (Part 1 has first name and last name with title name of Person 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": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "email",
"label": "Email (Part 2 begins from here with title Personal details)",
"type": "text",
"value": "",
"required": true,
"minlength": 3,
"maxlength": 20,
"order": 3
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "mobile",
"label": "Mobile Number",
"type": "text",
"value": "",
"required": true,
"order": 4
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "age",
"label": "Age",
"type": "text",
"value": "",
"required": true,
"order": 4
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "Father Name",
"label": "Father Name (Part 3 begins from here with Family details)",
"type": "text",
"value": "",
"required": true,
"minlength": 3,
"maxlength": 20,
"order": 5
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "mother_name",
"label": "Mother Name",
"type": "text",
"value": "",
"required": true,
"order": 6
}
];
Here everything works fine in generating the complete form..
But i would like to split up the form into Person Name, Personal details, Family Details with 2, 3, 2 input boxes respectively (the count may differ and its not static)..
A clear working example https://stackblitz.com/edit/angular-x4a5b6-geesde
In this example you can see that the Json is generated as complete form and unable to make the title in between where i want..
How can i make a split up in the form and initiate title for respective part..
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
-> Age
Family Details
-> Father Name
-> Mother Name
Kindly go through the demo, which has the file with JSON and kindly help me to split up the form like the above order.
I've once implemented something similar to what you are doing now. The idea is to create a special elementType for holding element groups.
The group config for person name, for example will look something like this:
const grouped: any = {
"elementType": "group",
"label": "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
}
]
};
For the group you will need to create a separate component that loops through the children. You should also consider the case where there are groups inside groups. So, you need to make it recursive.
E.g.
<div *ngFor="let question of questions" class="form-row">
<ng-container *ngIf="!question.children">
<app-question [question]="question" [form]="form"></app-question>
</ng-container>
<ng-container *ngIf="question.elementType === "group" && question.children && question.children.length > 0">
<app-dynamic-group [questions]="question.children" [form]="form"></app-dynamic-group>
</ng-container>
</div>
Inside group container component you actually do something very similar to what you do in the dynamic form so, you could combine the functionality:
<div *ngFor="let question of questions" class="form-row">
<ng-container *ngIf="!question.children">
<app-question [question]="question" [form]="form"></app-question>
</ng-container>
<ng-container *ngIf="question.elementType === "group" && question.children && question.children.length > 0">
<app-dynamic-group [questions]="question.children" [form]="form"></app-dynamic-group>
</ng-container>
</div>
Let me know if you need further explanation.
Here's a working version: https://stackblitz.com/edit/angular-x4a5b6-gwkc2z?file=src%2Fapp%2Fdynamic-group.component.html
If you want to Create sub form Component
Inside your top level form component place all you sub component
app.component.html
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<app-person></app-person>
<app-family></app-family>
<app-personal></app-personal>
<button class="btn btn primary">Save</button>
</form>
Use ControlContainer
ControlContainer
A base class for directives that contain multiple registered instances
of NgControl. Only used by the forms module.
ViewProviders to provide ControlContainer and use existing formGroupDiretive to get the parentForm then addformControl
app-person.component.ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, ControlContainer, FormGroupDirective } from '#angular/forms';
#Component({
selector: 'app-personal',
templateUrl: './personal.component.html',
styleUrls: ['./personal.component.css'],
viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
})
export class PersonalComponent implements OnInit {
personalForm;
constructor(private parentForm: FormGroupDirective) { }
ngOnInit() {
this.personalForm = this.parentForm.form;
this.personalForm.addControl('personal', new FormGroup({
email: new FormControl(''),
mobile: new FormControl(''),
age: new FormControl('')
}))
}
}
Example:https://stackblitz.com/edit/angular-ewdzmp
Related
I generate dynamic forms based on a json file. But they will be displayed among each other like this:
but it should be displayed like this (this is for example 3 inputs, if I have 4 the fourth one should be next to the third one and so on.. )
<div v-for="(item, index) in json" :key="index">
<b-form-input v-if="item.type" :type="item.type"></b-form-input>
</div>
my object:
[
{
"key": "key1",
"label": "Input Type Text",
"type": "text",
"value": ""
},
{
"key": "key2",
"label": "Input Type Number",
"type": "number",
"value": ""
},
{
"key": "key3",
"label": "Input Type Number",
"type": "number",
"value": ""
}
]
I've tried to solve it with class="row", but its not working because it's still the same that they are among each other..
is it possible that it should only "col-6" my input fields. I have also some selectoin and checkboxes... these I want to have "col-12"
Two ways I would approach this, depending on circumstances
First, if you can edit your input object and want more freedom in your classes you could do this:
Method 1
Edit your object to include classes
[
{
"key": "key1",
"label": "Input Type Text",
"type": "text",
"value": "",
"classes": "col-6"
},
{
"key": "key2",
"label": "Input Type Number",
"type": "number",
"value": "",
"classes": "col-6"
},
{
"key": "key3",
"label": "Input Type Number",
"type": "number",
"value": "",
"classes": "col-6 another-class"
}
]
Then use the classes in your html
<div class="row">
<div
v-for="(item, index) in json"
:key="index"
:class="item.classes"
>
<b-form-input v-if="item.type" :type="item.type"></b-form-input>
</div>
</div>
Method 2
Assign classes with a condition based on input type
<div class="row">
<div
v-for="(item, index) in json"
:key="index"
:class="{
'col-6': item.type === 'number' || item.type === 'text' || item.type === 'email',
'col-12': item.type === 'select' || item.type === 'checkbox',
}"
>
<b-form-input v-if="item.type" :type="item.type"></b-form-input>
</div>
</div>
answer based on s4k1b's
From the tags I can see you are using bootstrap
In bootstrap you can use row and col to align items side by side in a row.
<div class="row">
<div v-for="(item, index) in json" :key="index" class="col-6">
<b-form-input v-if="item.type" :type="item.type"></b-form-input>
</div>
</div>
I am making angular 6 application, where i am using angular dynamic form and the values are coming from JSON..
Simple JSON:
jsonData: any = [
{
"elementType": "dropdown",
"key": 'project',
"label": 'Choose option to display',
"options": [
{ "key": 'inputbox', "value": 'Show Project Level as input box' },
{ "key": 'dropdown', "value": 'Show Project Level as dropdown' }
],
"order": 1
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "project_level",
"label": "Project Level as input box",
"type": "text",
"value": "",
"order": 2
},
{
"elementType": "dropdown",
"key": 'project_level',
"label": 'Choose Project Level as dropdown',
"options": [
{ "key": 'low', "value": 'Low' },
{ "key": 'medium', "value": 'Medium' },
{ "key": 'high', "value": 'High' }
],
"order": 2
}
];
Requirement is going to be from this json only..
Take a look at Clear working example https://stackblitz.com/edit/angular-x4a5b6-5ys5hf,
You can see at initial stage i am having both input box and dropdwon.. But i need to have text box alone at first if i choose the Show Project Level as dropdown from first dropdown, then the project_level key needs to change as select box and vice versa will happen..
Order 1 has a dropdown in which i am having two options,
"options": [
{ "key": 'inputbox', "value": 'Show Project Level as input box' },
{ "key": 'dropdown', "value": 'Show Project Level as dropdown' }
],
If we choose the first option which has value as Show Project Level as input box, whereas if we choose second option that has value Show Project Level as dropdown..
Based on the above selection, i need to switch the form element accordingly, say user chosen Show Project Level as input box, then i need to display the input box,
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "project_level",
"label": "Project Level as input box",
"type": "text",
"value": "",
"order": 2
},
Whereas if user chosen Show Project Level as dropdown, then i need to display the dropdown,
{
"elementType": "dropdown",
"key": 'project_level',
"label": 'Choose Project Level as dropdown',
"options": [
{ "key": 'low', "value": 'Low' },
{ "key": 'medium', "value": 'Medium' },
{ "key": 'high', "value": 'High' }
],
"order": 2
}
So the key is going to be unique alone project_level but the form element alone needs to get changed based on the selection of either input box or selectbox..
Kindly help me to change the form element based on the selection on order 1 dropdown..
Result is expected only using pure angular and typescript/javascript way without jquery..
#Undefined, or use two variables "project_level_textbox" and "project_level_dropdown", and make visible or not hte form or make that question.controlType depending the value of the formControl (some like when you're making visible or not a question)
Add a new property "controlTypeAlternative" with "type","field" and "value" and make the
[ngSwitch]="question.controlTypeAlternative?
form.get(question.controlTypeAlternative.field).value==
question.controlTypeAlternative.value?
question.controlTypeAlternative.type:
question.controlType:question.controlType"
If some complex the operator ? but main that If has the property controlTypeAlternative, check if the value of the form.control is equal. if not use "controlType"
I am making angular dynamic form with form-elements loaded through JSON..
The form element generation are working fine, but i am in the need of change of form elements based on options selected from dropdown..
JSON that generates form-elements
jsonData: any = [
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "project_name",
"label": "Project Name",
"type": "text",
"value": "",
"required": false,
"minlength": 3,
"maxlength": 20,
"order": 1
},
{
"elementType": "dropdown",
"key": 'project',
"label": 'Choose option to display',
"options": [
{ "key": 'description', "value": 'Show Project Description' },
{ "key": 'level', "value": 'Show Project Level' },
{ "key": 'members', "value": 'Show Project Members' }
],
"order": 2
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "project_desc",
"label": "Project Description",
"type": "text",
"value": "",
"order": 3
},
{
"elementType": "dropdown",
"key": 'project_level',
"label": 'Choose Project Level',
"options": [
{ "key": 'low', "value": 'Low' },
{ "key": 'medium', "value": 'Medium' },
{ "key": 'high', "value": 'High' }
],
"order": 4
},
{
"elementType": "dropdown",
"key": 'project_members',
"label": 'Choose Project Member',
"options": [
{ "key": 'developer', "value": 'Developer' },
{ "key": 'leader', "value": 'Leader' },
{ "key": 'manager', "value": 'Manager' }
],
"order": 5
}
];
Based on the above JSON, the elements are generated..
Here you can see that Order 1 has textbox with project name which has no changes.
Then in next we have a dropdown with key as project, from here only the requirement starts..
In options, if i choose Show Project Description, then the Project Description textbox needs to be displayed and other two project_level and project_members needs to be in hidden format..
Likewise if i choose Show Project Level then the Project Level dropdown alone needs to be displayed and the Project Description and Project Members needs to be in hidden..
In the same way for Project Members..
So how to change the form-elements based on the selection of project dropdown values??
The working example for the same was here https://stackblitz.com/edit/angular-x4a5b6-5ys5hf
Kindly help me to hide the other elements based on the selection from the project dropdown using angular dynamic form alone..
As #benshabatnoam say the only thing you need is change you dinamic-form-question to include some like
<div [formGroup]="form" *ngIf="?????">
You can use a condition like #Benshabatnoam say, but I suggest you some more "parametrizable"
Imagine your json has a new property "visible" that was an object with two properties, field and value. So, your element "project_desc" can be like
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "project_desc",
"label": "Project Description",
"type": "text",
"value": "",
"order": 3,
"visible":{"field":"project","value":'description'} //<--add this "property"
},
So the dinamic-form-question can be like
<div [formGroup]="form"
*ngIf="!question.visible ||
form.get(question.visible.field).value==question.visible.value">
...
</div>
See that I include the condition (!question.visible) so, if you don't give the property "visible" to one field, this field is showed always.
Well, you must work some more, you must change question-base.ts to admit this new property
export class QuestionBase<T> {
value: T;
...
visible:any; //<--ad this property
constructor(options: {
value?: T,
...
visible?:any, //In constructor include "visible" too
..
}){
this.value = options.value;
...
this.visible = options.visible;
}
You can see your forked stackblitz
You need to make few changes to your code.
Change the json so that the options key will match the controls key.
...
{
"elementType": "dropdown",
"key": 'project',
"label": 'Choose option to display',
"options": [
{ "key": 'project_desc', "value": 'Show Project Description' },
{ "key": 'project_level', "value": 'Show Project Level' },
{ "key": 'project_members', "value": 'Show Project Members' }
],
"order": 2
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "project_desc",
"label": "Project Description",
"type": "text",
"value": "",
"order": 3
},
...
In your form add a *ngIf to the app-question component that will execute a function passing it the question, and this function will holding the logic for hiding the given question.
<app-question *ngIf="isShowQuestion(question)" [question]="question" [form]="form">
</app-question>
The function logic would check if the question is one of the controls you want to hide, and if so it will check the value of the dropdown 'option to display' for match, if match it will show the question else it will hide the question.
isShowQuestion(question: QuestionBase<any>): boolean {
// If one of the controls you want to hide
if (question.key === 'project_desc' ||
question.key === 'project_level' ||
question.key === 'project_members') {
// if the option to display have value and it is this question that show it else don't show it
return !this.form.controls['project'].value ||
this.form.controls['project'].value === question.key;
} else { // if not, always display
return true;
}
}
I've forked your stackblitz project, so you can see it in action here.
I am making angular application with angular dynamic form where i am loading data for dynamic form through JSON..
JSON has two parts such as part 1 and part 2,
jsonDataPart1: any = [
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "project_name",
"label": "Project Name",
"type": "text",
"value": "",
"required": false,
"minlength": 3,
"maxlength": 20,
"order": 1
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "project_desc",
"label": "Project Description",
"type": "text",
"value": "",
"required": true,
"order": 2
}
]
jsonDataPart2: any = [
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "property_one",
"label": "Property One",
"type": "text",
"value": "",
"required": true,
"order": 3
},
{
"elementType": "textbox",
"class": "col-12 col-md-4 col-sm-12",
"key": "property_two",
"label": "Property Two",
"type": "text",
"value": "",
"required": true,
"order": 4
},
{
"elementType": "radio",
"class": "col-12 col-md-3 col-sm-12",
"key": "property_required",
"label": "Property Required",
"options": [
{
"key": "required",
"value": "Required"
},
{
"key": "not_required",
"value": "Not Required"
}
],
"order": 5
},
{
"elementType": "checkbox",
"class": "col-12 col-md-3 col-sm-12",
"key": "property_check",
"label": "Property Required",
"order": 6
}
]
And i am loading the JSON as separate part like,
ngOnInit() {
//Building form first part
this.questions = this.service.getQuestions(this.jsonDataPart1)
this.form = this.qcs.toFormGroup(this.questions);
//Building form second part
this.questionsPartTwo = this.service.getQuestions(this.jsonDataPart2)
this.formPartTwo = this.qcs.toFormGroupPartTwo(this.questionsPartTwo);
}
And HTML looks like,
<div>
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<h4> <b> Form Part One begins from here </b> </h4>
<div *ngFor="let question of questions" class="form-row">
<ng-container>
<app-question [question]="question" [form]="form"></app-question>
</ng-container>
</div>
<h4> <b> Form Part Two begins from here </b> </h4>
<div *ngFor="let partTwo of questionsPartTwo">
<ng-container>
<app-question [question]="partTwo" [form]="formPartTwo"></app-question>
</ng-container>
</div>
<div class="form-row">
<button type="submit" [disabled]="!form.valid">Save</button>
</div>
</form> <br>
<pre>
{{form?.value|json}}
</pre>
</div>
I need to combine these two and need to get single output for the whole single form..
In my real application i am having two json data and loading each separately and assigning form so please dont break out the part one and part two json..
Let me stop the code here as its getting long you might get confused..
Here is a working stackblitz: https://stackblitz.com/edit/angular-x4a5b6-2rykpo
Just take a workaround dynamic-form.component.ts and dynamic-form.component.html You will get clear what i have done..
Kindly help me to load the splitted JSON to this.service.getQuestions and get two parts and join both in final output to submit..
If i am wrong in approach kindly help me to correct it as i am new in angular and dynamic form.. It needs to be in angular dynamic form and json loading only no change in it..
Help i am expecting is from combining both parts 1 and 2 while submitting form..
Please help me i am stucking for a long to come out of it..
Use Top level form and wrap your child form inside the parent form and use provider to use existing Form
Parent.component.html
<form [formGroup]="form" (ngSubmit)="onClick();">
<h1>Part1</h1>
<div class="row" formArrayName="part1" *ngFor="let c of form['controls']['part1']['controls'];let i =index">
<div class="col">
<input [attr.type]="jsonDataPart1[i].type"
class="form-control" [attr.placeholder]="jsonDataPart1[i].label"
[formControlName]="i" >
</div>
</div>
<app-part2 [part2]="jsonDataPart2">
<h1>Part2</h1>
</app-part2>
<br>
<button class="btn btn-primary">Save</button>
</form>
child.component.ts
import { Component, OnInit, Input } from '#angular/core';
import { FormGroup, FormControl, ControlContainer, FormGroupDirective, FormArray } from '#angular/forms';
#Component({
selector: 'app-part2',
templateUrl: './part2.component.html',
styleUrls: ['./part2.component.css'],
viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
})
export class Part2Component implements OnInit {
#Input('part2') part2;
part2Form;
constructor(private parentForm: FormGroupDirective) { }
ngOnInit() {
this.part2Form = this.parentForm.form;
const control = this.part2.map(d => new FormControl());
this.part2Form.addControl('part2F', new FormGroup({
part2: new FormArray(control)
}))
}
}
Example:https://stackblitz.com/edit/angular-xrrabj
You can take several aproach
1.-Create a formJoin that was {form1:questions of form1,form2:questions of form2}
In your ngOnInit
formJoin:FormGroup;
ngOnInit()
{
this.questions = this.service.getQuestions(this.jsonDataPart1)
this.form = this.qcs.toFormGroup(this.questions);
this.questionsPartTwo = this.service.getQuestions(this.jsonDataPart2)
this.formPartTwo = this.qcs.toFormGroup(this.questionsPartTwo);
this.formJoin=new FormGroup({form1:this.form,form2:this.formPartTwo})
}
//In your .html
<form (ngSubmit)="onSubmit()" [formGroup]="formJoin">
2.-Join the question in an unique json
this.allquestions=this.service.getQuestions(
this.jsonDataPart1.concat(this.jsonDataPart2));
this.formJoin2=this.qcs.toFormGroup(this.allquestions);
//get the first question.key in the second form
this.questionBreak=this.jsonDataPart2[0].key;
//In your .html
<form (ngSubmit)="onSubmit()" [formGroup]="formJoin2">
<h4> <b> An uniq Form </b> </h4>
<div *ngFor="let question of allquestions" class="form-row">
<!--make a break fro the secondForm-->
<ng-container *ngIf="question.key==questionBreak">
<h4> <b> Form Part Two begins from here </b> </h4>
</ng-container>
<ng-container>
<app-question [question]="question" [form]="formJoin2"></app-question>
</ng-container>
</div>
</form>
IMPORTANT NOTE: You needn't have two functions in service toFormGroup and toFormGroupPartTwo. If you see is the same function, you "feed" the function with different arguments, but is the same function.
In the stackblitz has the two options together
Update
update code to make a break,
I am completely new to Angular Schema forms and am trying to create some forms with it to display on my html pages. I would like to know how and where to place the form content and the schema content as defined in this page. In what kind of files do I place these lines of code and how can I call the form to be displayed in my html pages?
Form
[
"name",
"email",
{
"key": "comment",
"type": "textarea",
"placeholder": "Make a comment"
},
{
"type": "submit",
"style": "btn-info",
"title": "OK"
}
]
Schema
{
"type": "object",
"title": "Comment",
"properties": {
"name": {
"title": "Name",
"type": "string"
},
"email": {
"title": "Email",
"type": "string",
"pattern": "^\\S+#\\S+$",
"description": "Email will be used for evil."
},
"comment": {
"title": "Comment",
"type": "string",
"maxLength": 20,
"validationMessage": "Don't be greedy!"
}
},
"required": [
"name",
"email",
"comment"
]
}
Help in this regard with some detailed beginner level support guidelines will be much appreciated.
One way is to provide the schema and the instance data in a controller of your choice.
The documentation wiki provides a basic example where you should get all the necessary information.
Controller:
angular.module('myModule', ['schemaForm'])
.controller('FormController', function($scope) {
$scope.schema = {
type: "object",
properties: {
name: { type: "string", minLength: 2, title: "Name", description: "Name or alias" },
title: {
type: "string",
enum: ['dr','jr','sir','mrs','mr','NaN','dj']
}
}
};
$scope.form = [
"*",
{
type: "submit",
title: "Save"
}
];
$scope.model = {};
}
Template:
<div ng-controller="FormController">
<form sf-schema="schema" sf-form="form" sf-model="model"></form>
</div>