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.
Related
I have a chart that display the sales for Phone, Laptops and Printers and I can toggle any option on/off. I also have another toggle button "Without Taxes" that will display for each option (phone, laptops, printer) some new series.enter image description here
enter image description here
The big problem is that if I disable laptops & printer the "without taxes" toggle button will display all the lines not only the "phone without takes "
enter image description here
My implementations looks like this:
"series": [
{
"name": "Phones",
"id": "phonesId",
"template": "line",
"data": [
....
]
},
{
"name": "Laptops",
"id": "laptopId",
"template": "line",
"data": [
....
]
}
{
"name": "Printers",
"id": "printersId",
"template": "line",
"data": [
....
]
},
{
"name": "Without Taxes",
"id":"noTaxesId",
"template": "line-thin",
"data": [],
},
{
"name": "Phones No Taxes",
"linkedTo":"noTaxesId",
"template": "line-thin",
"data": [
....
]
},
{
"name": "Laptops No Taxes",
"linkedTo":"noTaxesId",
"template": "line-thin",
"data": [
....
]
}
{
"name": "Printers No Taxes",
"linkedTo":"noTaxesId",
"template": "line-thin",
"data": [
....
]
}
]
So I'm using an empty serie with id: "noTaxesId" and then I use "linkedTo":"noTaxesId" for the next 3 series.
Somehow I want to make the "Without taxes" toggle button to show only for the options that are active not for all of them all the time
I have an array of objects that I am using as a dataset in an interactive data dashboard. I want to add a new feature that displays data from only one object at a time, rather than pulling data from all objects (which I am already doing and it works well). As a test case, I have created a simple array:
var test1 = [
[{
"name": "Piece One",
"amount": 5
}, {
"name": "Piece Two",
"amount": 5
}, {
"name": "Piece Three",
"amount": 5
}],
[{
"name": "Piece One",
"amount": 1
}, {
"name": "Piece Two",
"amount": 1
}, {
"name": "Piece Three",
"amount": 5
}]
];
and the Vega-lite javascript:
var pieCreate = {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"title": "A pie chart",
"description": "A simple pie chart with embedded data.",
"width": "container",
"height": "container",
"data": {
"values": test1[0]
},
"mark": "arc",
"encoding": {
"theta": {
"field": "amount",
"type": "quantitative"
},
"color": {
"field": "name",
"type": "nominal",
"legend": null
}
}
};
This works, but I want the user to be able to choose which object to display (in the dashboard, each object contains data on different schools, and I want the user to be able to choose which school's data to display using a dropdown menu). My first thought was to set up a signal in the "data": {"values": field that would change the number in brackets to the array I want, but after a lot of trial and error, I think that may be a dead end. Signals should work to modify "field": "amount" and "field": "name" but I've tried every iteration of [0].amount that I can think of, while dropping the brackets from test1[0] and nothing has worked. If I can manage to access the object by directly referencing it in "field": I believe I can figure out the process using a signal and html form, but I'm starting to doubt if I'm even on the right track here.
I also tried the process outlined here in the vega-lite documentation: https://vega.github.io/vega-lite/tutorials/streaming.html, but it's doing something much more complicated than what I'm trying to do, and my javascript knowledge isn't sufficient to break it down to something usable. Does anyone have any ideas on how to make this work, using any of the above approaches (or a new, better one)?
You can use the vega Api's to change the data. On your selection, add a change event and on some conditions you can toggle between your data using those API's.
Refer the below snippet or fiddle:
var test1 = [
[{
"name": "Piece One",
"amount": 5
}, {
"name": "Piece Two",
"amount": 5
}, {
"name": "Piece Three",
"amount": 5
}],
[{
"name": "Piece One",
"amount": 1
}, {
"name": "Piece Two",
"amount": 1
}, {
"name": "Piece Three",
"amount": 5
}]
];
var yourVlSpec = {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"title": "A pie chart",
"description": "A simple pie chart with embedded data.",
"width": "350",
"height": "400",
"data": {
"values": test1[0]
},
"mark": "arc",
"encoding": {
"theta": {
"field": "amount",
"type": "quantitative"
},
"color": {
"field": "name",
"type": "nominal",
"legend": null
}
}
}
var view;
vegaEmbed("#vis", yourVlSpec).then(res => {
view = res.view;
});
function handleChange(a, b) {
var selectValue = document.getElementById("myselect").value;
if (selectValue == 'A') {
view.data('source_0', test1[0]);
} else {
view.data('source_0', test1[1]);
}
view.runAsync();
}
<script src="https://cdn.jsdelivr.net/npm/vega#5.20.2/build/vega.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite#5.0.0/build/vega-lite.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed#6.17.0/build/vega-embed.min.js"></script>
<select id="myselect" style="width:100px;" onchange="handleChange()">
<option>A</option>
<option>B</option>
</select>
<br>
<div id="vis"></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 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
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>