Array of Objects conditional style in textarea. Angular 6 - javascript

I have an array of objects like this:
list =[
{
name:"name1",
value:true
} {
name:"name2",
value:false
} {
name:"name3",
value:true
} {
name:"name4",
value:false
}
]
What I want to do is to show inside a text area all objects names and if object.value is false underline or bold the line. And then to be able to write inside the text area to remove the underlined elements.
What I tried was:
<textarea *ngFor="let item of list" [ngClass]="{cssClass: item.value==false}">
{{item.name}}
</textarea>
-The problem here is that it shows an empty text area for each object
AND
<div *ngFor="let item of list" [ngClass]="{cssClass: item.value==false}">
<textarea>
{{item.name}}
</textarea>
</div>
The problem here is that it creates a textarea per line, where the line is inside.

You have string value not the the boolean type. So you should use string value 'true' or 'false' not true and false.
<textarea *ngFor="let item of list" [ngClass]="{cssClass: item.value=='false'}">
OR
If you have option to modify the object then change the value type as -
list =[
{
name:"name1",
value:true
} {
name:"name2",
value:false
} {
name:"name3",
value:true
} {
name:"name4",
value:false
}
]

Related

Nested form array in angular

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);;
}
}

Create inputs as array in angular form

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!

How to append children to object dynamically

Suppose I have a object like as shown below:
var ob = [
{name: "root",id: 1},
{name: "root2",id: 2}
];
And I want to append children object dynamically to it. For example:
Suppose if I click on id 1 then children object should be appended to ob object.
var ob = [
{name: "root",id: 1, children: [
{name: 'sub1', id:'5'},
{name: 'sub2', id:'6'},
]
},
{name: "root2",id: 2}
];
Now if I click again on id 6 again children should be added to id 6.
var ob = [
{name: "root",id: 1, children: [
{name: 'sub1', id:'5'},
{name: 'sub2', id:'6', children: [
{name: 'subsub1', id:'8'},
{name: 'subsub2', id:'9'},
]
},
]
},
{name: "root2",id: 2}
];
I am trying to write a recursive function for it but no success. On click of any term I have reference only to the clicked term. I don't know about the parent term.
EDIT:
Below is my code:
<div *ngFor = "let term of terms">
<div class="row tr">
<a (click) = "showTerms($event)">{{term.id}}</a>
</div>
<div class="col-xs-6">{{term.desc}}</div>
<app-icd-codes *ngIf = "term.children" [terms] = "term.children"></app-icd-codes>
</div>
Here on click of a tag I am adding children's. So I need to create a dynamic object and update that object as shown above.
The most easy way is pass as argument the index of "terms". Put two buttons, one to AddTerms and another one to hideTerms/showTerms.
<div *ngFor = "let term of terms;let i=index">
<!--see the way to get the index of the array -->
<div class="row tr">
{{term.id}}
<!--you make a link, I use a button-->
<!--the button "add" is visible when there're NOT "children"-->
<button *ngIf="!term.terms" (click)="addTerms(i)">Add</button>
<!--the button to Show/hide is visible when there ARE "children"-->
<button *ngIf="term.terms" (click)="term.show=!term.show">
<span *ngIf="term.show">^</span>
<span *ngIf="!term.show">v</span>
</button>
</div>
<ng-container *ngIf ="term.terms && term.show">
<app-icd-codes [terms]="term.terms"></app-icd-codes>
</ng-container>
</div>
Then you must put your function addTerms. A simple function can be like
//see that you received the "index" of children
addTerms(index: number) {
this.terms[index].show = true; //<--to show the children
this.terms[index].terms = [{ id: 3 }, { id: 4 }]; //a "easy" way to add
}
Ok, really the function must be like
addTerms(index: number) {
let id=this.terms[index].id; //we get the "id"
//call a httpClient and subscribe
this.httpClient.get("url/"+id).subscribe(res=>
//in the subscription
{
this.terms[index].show = true; //<--to show the children
this.terms[index].terms = res
})
}
NOTE: Can result "strange" add new properties to an Object (in this case "children" and "show"). If we feel more confortable, we can add the properies when we create the object with a null value

How to filter second level ng-repeat in table

I have an array of objects which I want to display as a table with filter. My filter with model name is filtering the deep object family. This works fine but not the way I want it to work...
What I want: Insert string to input, e.g. 'Ma'. Now, I want it to display all items containing a string in family matching 'Ma' - that means I want to keep all the family members displayed as long as one string matches. In my example this would be the filtered result:
Homer Marge, Bart, Lisa, Maggie
Ned Maude, Rod, Todd
Example Code with snippet below:
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope) {
$scope.tableData = [
{id: 1, name: 'Homer', family: ['Marge', 'Bart', 'Lisa', 'Maggie']},
{id: 2, name: 'Carl', family: []},
{id: 3, name: 'Lenny', family: []},
{id: 4, name: 'Clancy', family: ['Sarah', 'Ralph']},
{id: 5, name: 'Ned', family: ['Maude', 'Rod', 'Todd']},
{id: 6, name: 'Moe', family: []}
];
});
table td {
padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<table>
<tr>
Filter family members: <input type="text" ng-model="name">
</tr>
<tr ng-repeat="item in tableData">
<td>{{item.name}}</td>
<td>
<span ng-repeat="member in item.family | filter: name">
{{member}}{{$last ? '' : ', '}}
</span>
</td>
</tr>
</table>
</div>
You need to filter the first ng-repeat:
<tr ng-repeat="item in tableData | filter: name && {family: name}">
The key is to provide the name of the property you want to filter on. Here it is the property family.
See fiddle (Type ma in the search box)
Edit: And remove the filter you placed on the inner ng-repeat
You can solve this without a custom filter, by providing a conditional filter on the first ng-repeat. For example:
<tr ng-repeat="item in tableData | filter: (name.length > 0 || '') && {family: name}">
You need the conditional because if you add a filter and remove it, the blank arrays wont be considered (angular quirk).
I have created a working fiddle here
You want to use a custom filter on the top ng-repeat. You can specify a function as the filter: custom filter answer. In this custom filter return all items in tableData that contain a match in the sub array.
Note: You could use lodash to help with this calculation.
Example: plunkr example
<tr ng-repeat="item in tableData | filter:criteriaMatch(name)">
...
$scope.criteriaMatch = function( criteria ) {
return function( item ) {
if(!angular.isDefined(criteria) || criteria === "")
return true;
var results = _.filter(item.family, function(n) {
if(_.contains(n.toUpperCase(), criteria.toUpperCase()))
{
return true;
}
});
if(results.length !== 0)
return true;
else return false;
};
};
Do you try to use the filter in tableData?? Something like this:
<input type="text" ng-model="family">
And
<tr ng-repeat="item in tableData | filter: family">

ng-options & ng-repeat: why angular model isn't bind?

When I bind the select elements to ng-model family, which refers to an element of font.families array, Angular doesn't bind what is selected. The array stays empty, with just the first null element.
But when I bind the select elements to font.families[$index], the binding is effective.
// Javascript
$scope.font.families = [null]; // Used to create an empty first line in the form
$scope.families = [
{ name: "Foo", parent: "Bar" },
{ name: "Doo", parent: "Dar" }
];
$scope.mainFamilies = [
{ name: "Bar", children: ["Foo", "Baz"] },
{ name: "Dar", children: ["Doo", "Hoo"] }
];
<!-- HTML -->
Font families : {{ font.families }}
<div ng-repeat="family in font.families track by $index">
Family : {{ family }}
<ng-form name="familyRow">
<select ng-options="fam as fam.name group by fam.parent for fam in families"
ng-model="font.families[$index]"
name="family">
<option disabled value="">—</option>
</select>
<select ng-options="mainFam.name as mainFam.name for mainFam in mainFamilies"
ng-model="font.families[$index].parent"
name="mainFamily">
<option disabled value="">—</option>
</select>
</div>
Plunker to illustrate the problem.
How comes?
Here's my understanding of the issue:
Case 1 (Working): You're using ng-model="model.families[$index]". In this case when you make a selection here's what happens:
model.families[0] -> Your selection
Case 2 (Not working): You're using ng-model="family". In this case this is what is happening:
family -> model.families[0] -> null
When you make the selection:
family -> new selection
model.families[0] -> null
The problem is that in 2nd case, the assignment causes the family variable to be assigned a new value altogether and the source array remains the same.
You can further verify this by adding these statements in your html:
family: {{family}}
model.families: {{model.families[$index]}}
Are they equal?: {{familly === model.families[$index]}}

Categories

Resources