Tried my level best please help me out..
I am making an angular dynamic form with a form splitting into total of three parts in which the two parts were made already. Now I am making part three which will be generated by selecting an option from dropdown...
Even those part also is done...
But I am unable to make a form array in it... As I am new in angular please help me.
HTML:
Form part 3 will be here which will be array
<select multiple (change)="changeEvent($event)">
<option *ngFor="let opt of persons" [value]="opt.key">{{opt.value}}</option>
</select>
<div *ngFor="let item of array">
{{item.value}} is the parent
<div *ngFor="let child of item.templateChild">
{{child.property_name}}
<div *ngFor="let partThree of questionsParthree">
<ng-container>
<app-question [question]="partThree" [form]="formPartThree"></app-question>
</ng-container>
</div>
</div>
</div>
Select Box change event:
changeEvent(e) {
if (e.target.value == 1) {
this.array = [];
this.array.push(
{
key: 1, value: "Template one",
templateChild: [
{ property_name: "Property one" },
{ property_name: "Property two" }
]
}
);
let propertiesArray = [];
this.array.forEach(element => {
element.templateChild.forEach(data => {
propertiesArray.push(
{
key: data.property_name,
label: data.property_name,
"elementType": "textbox",
"type": "text"
}
)
});
});
this.questionsPartThree = this.service.getQuestions(propertiesArray);
this.formPartThree = this.qcs.toFormGroup(this.questionsPartThree);
this.formJoin = new FormGroup({ form1: this.form, form2: this.formPartTwo, form3: this.formPartThree });
}
}
Continuation like part 1 and 2..
I have posted the code related to creating the form array alone..
Updated Stackblitz https://stackblitz.com/edit/angular-x4a5b6-mnyifs
Here if we select any option then you will get the input boxes with values..
I would like to create array with it like,
If we select the first option Template One, Output expected is exactly as like
"templateChild" : [
{"property_one": "", "property_two":""}
]
So the final output of whole form going to be if i select Template One and also Template Two from select box (as select box is multi select),
{
"form1": {
"project_name": "",
"project_desc": ""
},
"form2": {
"property_one": "",
"property_two": ""
},
"template_details" : [
{ "template_name": "Template One",
"templateChild" : [{"property_one": "", "property_two":""}]
},
{ "template_name": "Template Two",
"templateChild" : [{"property_three": "", "property_four":"",
"property_five":""}]
}
]
}
Have a work around in the demo i have provided and give me a better solution..
Kindly please help me to create a form as like the above when we select an option from dropdown.. If i am wrong in my approach also please correct me..
If i finish this third part then everything will get alright any angular technical expert please help me..
Taking too long please help me out..
You can dynamically change an AbstractController inside a FormGroup using the setControl() method.
Add an empty form3 part for the moment
this.form3 = new FormGroup({});
this.formJoin = new FormGroup({ form1: this.form, form2: this.formPartTwo, form3: this.form3 })
When selecting an item, generate a new FormGroup according the form you create.
if (e.target.value == 1) {
this.array = [];
this.form3 = new FormGroup({'Property one': new FormControl('insert whatever you want')});
this.formJoin.setControl('form3', this.form3);
You should be able to do something what that start!
Related
I'm creating multiple input fields with checkboxes in vue and I want that only one can be true. So if the user clicks on one the others should be false so that only the last clicked checkbox is true.
My code is like that:
new Vue({
el: "#app",
data: {
selected: null,
options: [
{"id": 1, "title": "One", "value": false},
{"id": 2, "title": "Two", "value": false },
{"id": 3, "title": "Three", "value": false},
]
},
watch: {
selected(selected) {
this.options.forEach((option, index) => {
option.id == selected ? option.value = true : option.value = false;
});
}
}
Unfortunately my watcher isn't working properly. I would be really glad if somebody can show me how to correct it. I want that always the last true element is the only true element and the watches sets all other elements in options to false.
If i understood your requirements correctly you can still do it with radio buttons. You can specify the value to be used inside the selected variable, as described here: https://v2.vuejs.org/v2/guide/forms.html#Radio-1. This means that you can set up a watcher and then mutate the options list accordingly:
selected: function (newVal) {
this.options.forEach(option => {
if (option.id === newVal) option.value = true
else option.value = false
})
console.log(this.options)
}
Here is a sandbox to see it in action:
https://codesandbox.io/s/heuristic-goldberg-lilsw
Update: Just saw that you want to use the </b-switch> from buefy. You can still do something similar by calling a function from the input event which then mutates the options list according to the just changed element. Something like this:
<div v-for="(option,index) in options" :key="index">
<div class="box">
<div class="field">
<b-switch v-model="option.value" #input="(modelValue) => onSwitchChanged(modelValue, option.id)">
{{ option.title }}
</b-switch>
<label :for="index">{{ option.title }}</label>
</div>
</div>
</div>
function onSwitchChanged(modelValue, id) {
if (!modelValue) return
this.options.forEach(option => {
if (option.id === id) option.value = true
else option.value = false
})
}
If you want that Only One will be selected then you have to use radio button. Checkbox has options to select all But One by One.
Without watch you can use methods. Pass index to the method.
<input
type="checkbox"
:id="index"
:value="option.id"
#click="selectAnOption(index)"
>
Method:
methods: {
selectAnOption(index) {
this.options[index].value = true
}
}
Full Code here: https://jsfiddle.net/8ktdp9ew/
I am making angular application with reactive form, where i have made a nested form array which will get nested on button click.
A clean working example https://stackblitz.com/edit/angular-thhczx , it has static inputs and hence on click over Add new template, it will add a another nested part and for Add new property, it will generate another property array..
So you had got the above working example concept right??
I would like to have the same json but not with add button and with dropdown.
Html of dropdown:
<select multiple [(ngModel)]="selectItems" (change)="changeEvent($event)">
<option *ngFor="let template of templates" [value]="template.key">{{template.value}}</option>
</select>
{{selectItems|json}}
<form [formGroup]="form">
<div *ngFor="let item of array">
{{item.value}} is the parent
<div *ngFor="let child of item.templateChild">
{{child.property_name}}
<input type="text" [value]="child.property_name">
</div>
<br><br><br>
</div>
</form>
<br><br><br>
{{form.value|json}}
Templates array: which gives value for dropdown
templates = [
{
key: 1, value: "Template one",
templateOneChild: [
{ property_name: "Property one" },
{ property_name: "Property two" }
]
},
{
key: 2, value: "Template two",
templateTwoChild: [
{ property_name: "Property three" },
{ property_name: "Property four" },
{ property_name: "Property five" }
]
},
{
key: 3, value: "Template three",
templateThreeChild: [
{ property_name: "Property six" },
{ property_name: "Property seven" }
]
}
]
Also made a stackblitz link for the above https://stackblitz.com/edit/angular-1sg5cv
Here if i select the option template one and template two (as the selectbox is multi select) from the dropdown then i am expecting the output as,
"template_details" : [
{ "template_name": "Template One",
"template_data" : [{"property_one": "", "property_two":""}]
},
{ "template_name": "Template Two",
"template_data" : [{"property_three": "", "property_four":"",
"property_five":""}]
}
]
If you click over the two options of template one and two you can see that you will get two and three input boxes respectively...
I wish to generate the input boxes with property names automatically under each template on selection of dropdown values..
So in simple need to convert dropdown demo as like the static inputs with add button with the same nested json structure.
I kindly request angular experts to help me in generation of input boxes based on property names for the selected template's..
I did my level best in it unable to get the solution please help me to form nested array json on based on selection of dropdown..
#Undefined, you need two different jobs
Create a formGroup
Display inputs that manage the formGroup
the first part is the easer. Go step by step, if you select template one, you need some like
this.fb.group({
template_name:"template one",
template_data:this.fb.array([
this.fb.group({
property_one:'',
property_two:''
})
])
})
but you want to do the things dinamically, so, make a function that receive an object and return a FormGroup. As you only need the "value" of the template and the childs, your function can be like
createFormGroup(value:string,children:any[]):FormGroup
{
/*e.g. for template one, you send
value: "Template one",
children: [
{ property_name: "Property one" },
{ property_name: "Property two" }
]
*/
let controls:FormGroup[]=children.map(
(x:any)=>this.fb.group({
[x.property_name]:''
})
)
return this.fb.group({
template_name:value,
template_data:this.fb.array(controls)
})
}
So yet we can create a formGroup for the differents templates and join in a FormArray
changeEvent(e) {
let arrayControls:FormGroup[] = [];
//in this.selectItems we have, e.g. [1,3]
for (let select of this.selectItems) {
//search the template, select will be e.g. 1,3
let template:any=this.templates.find(x=>x.key==select);
switch (+select) {
case 1:
arrayControls.push(this.createFormGroup(template.value,template.templateOneChild));
break;
case 2:
arrayControls.push(this.createFormGroup(template.value,template.templateTwoChild));
break;
case 3:
arrayControls.push(this.createFormGroup(template.value,template.templateThreeChild));
break;
}
}
this.form=this.fb.group({
template_details:this.fb.array(arrayControls);
})
}
See that if all ours children of templates was under a property "children" (not templateOneChild for the first, templateTwoChild for the seconds...) our function becomes in
changeEvent(e) {
let arrayControls:FormGroup[] = [];
//in this.selectItems we have, e.g. [1,3]
for (let select of this.selectItems) {
//search the template, select will be e.g. 1,3
let template:any=this.templates.find(x=>x.key==select);
arrayControls.push(this.createFormGroup(template.value,template.children));
}
this.form=this.fb.group({
template_details:this.array(arrayControls);
})
}
Well you have the "form" created, now is time to show it. The form is like
<div *ngIf="form">
<form [formGroup]="form">
<div formArrayName="template_details">
<div *ngFor="let item of details.controls;let i=index" [formGroupName]="i">
<input formControlName="template_name">
<div formArrayName="template_data">
<div *ngFor="let child of item.get('template_data').controls;let j=index" [formGroupName]="j">
<input formControlName="??????????">
</div>
</div>
</div>
</div>
</form>
</div>
Yes, we have a problem, we don't know the "formControlName" of the inner formArray. One solution is have a variable "controlsName" that will be an array of array, so, if e.g. we choose 1 and 3 template our controlsName was like
controlsName=[
["property_one","property_two"],
["property_six",property_seven"]
]
Well, again make a function that return an array of strings with the names of the properties. it's a simply version of our createFormGroup, receive "children" and return an array of strings.
getControlNames(children:any[]):string[]
{
let controlNames:string[]=children.map(x=>x.property_name);
return controlNames;
}
Well, in changeEvent we call to this function after call to createFormGroup
changeEvent(e) {
let arrayControls:FormGroup[] = [];
let controlsName:string[] = []; //<--add this line
for (let select of this.selectItems) {
let template:any=this.templates.find(x=>x.key==select);
switch (+select) {
case 1:
arrayControls.push(this.createFormGroup(template.value,template.templateOneChild));
controlsName.push(this.getControlNames(template.templateOneChild)); //<--and this
break;
... idem with case 2 and case 3...
}
}
this.controlsName=controlsName; //<--give value to name first
//then create the form
this.form=this.fb.group({
template_details:this.fb.array(arrayControls);
})
After this, replace the < input formControlName="??????????" > by
<input [formControlName]="controlsName[i][j]">
See that we use [formControlName] (not formControlName) because is an evaluated expression.
See the stackblitz here
I am not sure about your question.You want to dynamically add controls using json.
Reference link : https://angular.io/guide/dynamic-form
Working example : https://stackblitz.com/edit/angular-srpk3w
Replace your files with the below code :
app.component.html
<select multiple [(ngModel)]="selectItems" (change)="changeEvent($event)">
<option *ngFor="let template of templates" [value]="template.key">{{template.value}}</option>
</select>
{{selectItems|json}}
<div *ngIf="form">
<form [formGroup]="form">
<div *ngFor="let item of array">
{{item.value}} is the parent
<div *ngFor="let child of item.templateChild; index as i">
{{child.property_name}}
<input type="text" formControlName="{{child.property_name.split(' ').join('_')}}" [value]="child.property_name" >
</div>
<br><br><br>
</div>
</form>
</div>
<br><br><br>
{{form.value|json}}
app.component.ts
import { Component } from '#angular/core';
import { FormControl, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
array: any[] = [];
selectItems: any;
form: FormGroup;
templates = [
{
key: 1, value: "Template one",
templateChild: [
{ property_name: "Property one" },
{ property_name: "Property two" }
]
},
{
key: 2, value: "Template two",
templateChild: [
{ property_name: "Property three" },
{ property_name: "Property four" },
{ property_name: "Property five" }
]
},
{
key: 3, value: "Template three",
templateChild: [
{ property_name: "Property six" },
{ property_name: "Property seven" }
]
}
]
changeEvent(e) {
this.array = [];
for (let select of this.selectItems) {
this.array.push(this.templates[select-1])
this.form=this.getFormValue(this.array);
}
}
getFormValue(array){
let group: any = {};
array.forEach(r=>{
r.templateChild.forEach((t,index)=>{
group[t.property_name.replace(/ /g, "_")]= new FormControl(t.property_name);
})
})
return new FormGroup(group);;
}
}
On my page I have angular ui accordion, inside of each panel, I'm rendering list with items and checkboxes, also I have checkbox "select all".
For selection method and logic I used this resource. In this resource logic seems working, but however I'm putting this logic to my code, it stops working.
What I want to achieve is when all checkboxes are selected, checkbox "select all" has been selected automatically, and if some of checkboxes is unselect, checkbox "select all" has to be unselect as well.
I have tried multiple suggestions provided here, here, here, but in the end I'm getting the same result.
I appreciate if somebody could help me to resolve my problem.
$scope.categories = [
{
id: 1,
name: "category 1"
},
{
id: 2,
name: "category 2"
},
{
id: 3,
name: "category 3"
}
]
$scope.selectedAll = false;
$scope.selectAll = function(array) {
$scope.selectedAll = !$scope.selectedAll;
angular.forEach(array, function(item) {
item.Selected = $scope.selectedAll;
});
};
$scope.checkIfAllSelected = function(array) {
$scope.selectedAll = array.every(function(item) {
return item.Selected == true
})
};
html
<div>
<div class="row" ng-class="{'selected': selectedAll, 'default': !selectedAll}">
<div>Select all
<input type="checkbox"
ng-model="selectedAll" ng-click="selectAll(categories)" >
</div>
</div>
<div class="row" ng-repeat="item in categories | orderBy : 'id'" ng-class="{'selected': item.selected, 'default': !item.selected}">
<div > {{ item.name }}
<input type="checkbox"
ng-model="item.Selected" ng-click="checkIfAllSelected(categories)"
>
</div>
</div>
This is my plunker
Please take a look at this fork of your plunker: https://plnkr.co/edit/OW3F1VMke9iLuNkt5p3o?p=preview
Two things:
1. It's a good practice to create an object to your view model (you can find it under the name model in the plunker $scope.model. This will solve 2 way data binding issues.
2. I have changed the ng-click to ng-change (this is not part of the solution though - its just more correct in my opinion).
Please let me know if you need more clarifications.
i'm kind of new in Angular, and I facing a problem, I didn't find an answer that could help me, if some one could, thank you. :)
I have a json like this:
"items": [
{
"post_type": "release",
"label": "Releases"
},
{
"post_type": "news",
"label": "Notícias",
"options": [
{
"id": 1,
"name": "Galeria de Fotos"
},
{
"id": 2,
"name": "Agência de Notícias"
},
{
"id": 3,
"name": "Rádio"
},
{
"id": 4,
"name": "TV"
}
]
},....
And I m getting data like:
var _preparingPostTypes = function ($scope) {
$scope.post_types = [];
PostTypeService.getPostTypes().then(function (data) {
$scope.post_types = data.data;
});
};
What I want to do is create 2 selects, 1st - with the post_type ('release', 'news') and a second one with the 'options' array from a post_type, that is only visible when select an option with the 'options' in the array like 'news'. I did something like this, where I can get the post_type like a charm, but I don't know how to proceed:
<div class=form-group>
<label>Pesquisar em:</label>
<select title="Pesquisar em:" class="form-control select-post-type"
ng-model="widget.post_type"
ng-options="item.post_type as item.label for item in post_types.items"></select>
</div>
EDIT:
In my request I need to pass the post_type string to server, from the first select, so the ng-options is:
ng-options="item.post_type as item.label for item in post_types.items
Not:
ng-options="item as item.label for item in post_types.items
Thanks everybody!
You can add an ng-change to the first select that calls a function that loads the options
<select title="Pesquisar em:" class="form-control select-post-type"
ng-model="widget.post_type"
ng-options="item as item.label for item in post_types.items" ng-change="typeChanged()"></select>
And the function would load inside $scope.options the selected type options. Then you iterate over $scope.options in the second select
UPDATE:
I haven't tested the code, but it may guide you
Select:
<select title="Options:" class="form-control select-post-type"
ng-model="widget.option"
ng-options="option as option.name for option in options">
Change function (triggered when the value of the first select changes, so it will have the responsibility of loading the options of the post_type selected):
$scope.typeChanged = function() {
for (var i = 0; i < $scope.post_types.items.length; ++i) {
if ($scope.post_types.items[i].label == $scope.widget.post_type) $scope.options = $scope.post_types.items[i].options || [];
}
}
widget.post_type should contain the selected item. So the other select can have options like option as option.name in widget.post_type.options. Note that you should probably have an options collection on each item if you want this to work.
You can do this very easily using following technique. You just need to use the ng-model from the first select. See my approach:
<label>Pesquisar em:</label>
<select title="Pesquisar em:" class="form-control select-post-type"
ng-model="firstSelect"
ng-options="item as item.label for item in post_types.items"></select>
<select title="Second one" class="form-control select-post-type"
ng-model="secondSelect" ng-show="firstSelect.options"
ng-options="option as option.name for option in firstSelect.options"></select>
Im currently trying to nest a <select> inside a div with a ng-repeat
something like this:
<li ng-repeat="item in items >
<div class="row">
<div class="col-xs-7">
<span ng-bind="item.Name"></span>
</div>
<div class="col-xs-4 modal-body--ul--li__dropdown_container">
<select ng-model="typemodel" >
<option ng-repeat="usertype in item.userGroups"></option>
</select>
</div>
<div class="col-xs-1">
<a ng-click="add(item)"></a>
</div>
</div>
</li>
In the controller im adding the item to a new list with selected items. I also want to add the value from the dropdown as a new value to the list in the controller.
$scope.add = function (item) {
//New value with the value from the dropdown
item.recipientType = typemodel;
$scope.selected.push(item);
};
The list :
$scope.items = [
{
"Name":"Name1",
"userGroups":[
{ "type": "gruop1" },
{ "type": "gruop2" },
{ "type": "group3" }
]
},
{
"Name":"Name2",
"userGroups":[
{ "type": "gruop1" },
{ "type": "gruop2" },
{ "type": "group3" }
]
}
]
In short i need the selected value from the dropdown when i hit the "add", the value of the current typemodel.
This is how it looks now, but it dont work. I know i have to get the correct model and maybe track the items list by $index, but after searching the web the whole day for a solution, im still stuck!
All help is appreciated.
-Thanks!
ps. please comment if something is missing from the question, or something more is needed to solve this.
First, change your dropdown list to
<select ng-options="usertype in item.userGroups" ng-model="item.recipientType">
Then when you trigger the add function, item's property recipientType is already set.
$scope.add = function (item) {
console.log(item.recipientType);
$scope.selected.push(item);
};