Formgroup binding in select boxes with Angular - html

I have a nested JSON array which i have to iterate to HTML using formgroup or formarray. This response is to be iterated into dynamically created select boxes depending on the length of array.
The JSON response coming in is:
var result = [{
id: 1,
options: [
{ option: 'Ram', toBeSelected: false },
{ option: 'Ravi', toBeSelected: true }
]
},
{
id: 2,
options: [
{ option: 'Pooja', toBeSelected: false },
{ option: 'Prakash', toBeSelected: false }
]
}
]
I have to iterate this into HTML in such a way that if any of these options have toBeSelected as true, that option should be preselected in HTML and if not, placeholder text can be shown.

According to the JSON in question, you can make it like:
ngOnInit() {
this.form = this._FormBuilder.group({
selections: this._FormBuilder.array([])
});
// the api call should be made here
this.jsonResponse.map(item => {
const opts = item.options.filter(o => {
return o.toBeSelected
});
if (opts.length) {
this.addSelections(opts[0].option);
} else {
this.addSelections();
}
});
}
get selections() {
return this.form.get('selections') as FormArray
}
addSelections(value?: string) {
this.selections.push(
this._FormBuilder.control((value ? value : ''))
);
}
Live view here.
Stackblitz link: https://stackblitz.com/edit/dynamic-form-binding-kx7nqf

Something along the lines of this?
<div *ngFor="let result of results">
<p>ID - {{ result.id }}</p>
<div *ngFor="let option of result.options">
<input
type="checkbox"
[checked]="option.toBeSelected">
{{ option.option }}
</div>
</div>
This isn't an example of FormGroup though but should help you understand how it can be done.
Sample StackBlitz

Related

Is there any way to bind jsonpath dynamically in angular 6

I am creating a reusable component in which I can pass any dynamic json so he'll able to handle that
what I tried is
mycomponent.html:
<div *ngFor="let Node of items">
{{ Node[getPath()]}}<br>
</div>
mycomponent.ts:
#Input()
path:any;
#Input()
items:any;
getData(){
var String="";
var data=this.path.split('.');
for(var i=0;i<data.length;i++){
if(i==0){
String+="'"+data[i]+"']";
}else if(i+1==data.length){
String+="['"+data[i]+"'";
}
else{
String+="['"+data[i]+"']";
}
}
return String;
}
The output of getData() function is like
'related']['name'
because the reasone is
in mycomponent.html i am using
{{Node[getData()]}}
so basically im trying to do is
{{Node['related']['name']}}
MainComponent.html:
<my-component path="related.name" items={{items}}></my-component>
Input Array
[
{
"related": [
{
"name": "first"
}
]
},
{
"related": [
{
"name": "second"
}
]
}
]
my expected output is like
first
second
I want to create that component as resuable so i can use anywhere in my project so give any suggestion to do that :(
When you create a dynamic component to handle the input JSON.
You could do two ways
Create the component.
mycomponent.html:
mycomponent.ts
mycomponent
#Input()
items:any;
When you integrate the component as any child then you could do as
<mycomponent [items]="items"></mycomponent>
or when you want to invoke as popup
then
let ngModelRef : NgbModalRef = this.ngbModalService.open(mycomponent );
ngModelRef.componentInstance.items= items;
return ngModelRef;

How to solve Angular [object object] in template

I am getting [object object] showing in my html page.
I am calling the a web service and in my console.log. I can see the full response but on my template I only see [object object]
test() {
this.http.post('api...', JSON.stringify(this.getAllCaseInfoWithCAS)).subscribe(data => {
this.testData = [data];
});
}
I have a ngFor
<ng-template ngbPanelContent>
<div class="row" *ngFor="let item of testData">
{{item.getAllCaseInfoWithCASReturn.caseDetails}}
</div>
</ng-template>
The response looks like this
{
"getAllCaseInfoWithCASReturn": {
"caseDetails": {
"caseNumber": "12345",
"componentDetails": {
"component": "29"
},
"personNumber": "5",
"month": "7"
}
}
}
How can I display all the information on the template?
You can use JsonPipe
{{ value_expression | json }}
Converts a value into its JSON-format representation. Useful for debugging.
{{ item.getAllCaseInfoWithCASReturn.caseDetails | json }}
Better start by making JSON formatted as an array of caseDetails, here is a rough code of what you can do with current JSON:
// This should be refactored to a service file.
getCaseDetails(data: any): Observable<any[]> {
const url = `api/url`;
return this.httpClient.post(url, data)
.pipe(
map(result => result['getAllCaseInfoWithCASReturn']['caseDetails']),
tap(caseDetail => console.log(caseDetail))
)
}
test() {
this.getCaseDetails(dummyData).subscribe(caseDetail => {
this.testData = caseDetail;
})
}
<ng-template ngbPanelContent>
<div class="row">
{{testData?.caseNumber}} <!-- or json pipe -->
{{testData?.personNumber}}
{{testData?.month}}
{{testData?.componentDetails?.component}}
</div>
</ng-template>

How to v-model 2 values?

I was using buefy <b-autocomplete> component and there is one property called v-model which is binding values to the input field
now I wanna bind Full Name into the field but the data consist with list[index].first_name and list[index].last_name, and the index is from a v-for loop.
Since v-model cannot bind a function (it has specific index so I cannot just concat it on computed then pass it on) so it's either v-model="list[index].first_name" or v-model="list[index].last_name"
How do I make it bind's these two?
You need a settable computed for full name, and you can v-model that. You just have to decide on a rule for where extra spaces go and what to do if there is no space.
new Vue({
el: '#app',
data: {
firstName: 'Joe',
lastName: 'Smith'
},
computed: {
fullName: {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(newValue) {
const m = newValue.match(/(\S*)\s+(.*)/);
this.firstName = m[1];
this.lastName = m[2];
}
}
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
First: {{firstName}}<br>
Last: {{lastName}}<br>
Full Name: <input v-model="fullName">
</div>
I am not sure if i get the question,but i am assuming that you have a list of names and last names and you want to give the ability to user to change those proprties of list.For more See the example in action
The "html" part
<div id="app">
<template v-for="item in list" :key="list.id">
<input type="text" :value="item.name" #input="changeList($event, item.id, 'name')">
<input type="text" :value="item.last_name" #input="changeList($event, item.id, 'last-name')">
Your full name is {{item.name}} {{item.last_name}}
<hr>
</template>
</div>
The "javascript(vue)" part
new Vue({
el: "#app",
data: {
list: [
{ id: 1, name: "name1", last_name: 'last_name 1' },
{ id: 2, name: "name2", last_name: 'last_name 2' },
{ id: 3, name: "name3", last_name: 'last_name 3' },
{ id: 4, name: "name4", last_name: 'last_name 4' }
]
},
methods: {
changeList(event, id, property) {
let value = event.target.value
for (item of this.list) {
if (item.id === id) {
if(property === 'name') {
item.name = value
}else if (property === 'last-name') {
item.last_name = value
}
}
}
}
}
})
As it's been said you can't use 2 values in v-model
But if you want to use <b-autocomplete> that means you already have the data and you can compute it in any way you want.
If you have an array of user objects (with firstname and lastname for example) you can do something like:
data() {
return {
users: [
//...
],
computedUsers: [],
}
},
mounted() {
this.users.forEach(user => {
this.computedUsers.push(user.firstname + ' ' + user.lastname)
})
}
and use this new array in the filteredDataArray (from the Buefy autocomplete example)
Fiddle example here

How I get $key when I using ngFor using angularfire2?

I have some question, example I have a json file:
{
"items" : {
"khanh" : {
"name":"2017 shirt",
"size":["S","M","L"],
"image":"http://placehold.it/650x450",
"likes":0,
"price":123
},
"-KdleehAQm0HgVFYdkUo" : {
"name":"2017 shirt",
"size":["S","M","L"],
"image":"http://placehold.it/650x450",
"likes":0,
"price":123
},
"-Kdlg3AqKNTnbhjAVT8h" : {
"name":"2017 shirt",
"size":["S","M","L"],
"image":"http://placehold.it/650x450",
"likes":0,
"price":123
}
}
}
I have a button that update how many likes. But I have a problem. I don't know how to get the "key". Example:"khanh","-KdleehAQm0HgVFYdkUo"
This worked for me.
Component:
import { AngularFireDatabase, FirebaseListObservable } from 'angularfire2/database';
dbcomplextest: FirebaseListObservable<any[]>;
constructor(afDB: AngularFireDatabase) {
this.dbcomplextest = afDB.list('/complex/');
}
View:
<span *ngFor="let value of dbcomplextest | async">
{{ value.$key }} - {{ value.$value }}
</span>
In the latest version >=4.6.0, you have to use snapshotChanges().map() to store the key.
this.itemsRef = db.list('messages');
// Use snapshotChanges().map() to store the key
this.items = this.itemsRef.snapshotChanges().map(changes => {
return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
});
Then now you can be able to get it from your view list using .key

Angular 2: Get Values of Multiple Checked Checkboxes

My problem is really simple: I have a list of checkboxes like this:
<div class="form-group">
<label for="options">Options :</label>
<label *ngFor="#option of options" class="form-control">
<input type="checkbox" name="options" value="option" /> {{option}}
</label>
</div>
And I would like to send an array of the selected options, something like:
[option1, option5, option8] if options 1, 5 and 8 are selected. This array is part of a JSON that I would like to send via an HTTP PUT request.
Thanks for your help!
Here's a simple way using ngModel (final Angular 2)
<!-- my.component.html -->
<div class="form-group">
<label for="options">Options:</label>
<div *ngFor="let option of options">
<label>
<input type="checkbox"
name="options"
value="{{option.value}}"
[(ngModel)]="option.checked"/>
{{option.name}}
</label>
</div>
</div>
// my.component.ts
#Component({ moduleId:module.id, templateUrl:'my.component.html'})
export class MyComponent {
options = [
{name:'OptionA', value:'1', checked:true},
{name:'OptionB', value:'2', checked:false},
{name:'OptionC', value:'3', checked:true}
]
get selectedOptions() { // right now: ['1','3']
return this.options
.filter(opt => opt.checked)
.map(opt => opt.value)
}
}
I have find a solution thanks to Gunter! Here is my whole code if it could help anyone:
<div class="form-group">
<label for="options">Options :</label>
<div *ngFor="#option of options; #i = index">
<label>
<input type="checkbox"
name="options"
value="{{option}}"
[checked]="options.indexOf(option) >= 0"
(change)="updateCheckedOptions(option, $event)"/>
{{option}}
</label>
</div>
</div>
Here are the 3 objects I'm using:
options = ['OptionA', 'OptionB', 'OptionC'];
optionsMap = {
OptionA: false,
OptionB: false,
OptionC: false,
};
optionsChecked = [];
And there are 3 useful methods:
1. To initiate optionsMap:
initOptionsMap() {
for (var x = 0; x<this.order.options.length; x++) {
this.optionsMap[this.options[x]] = true;
}
}
2. to update the optionsMap:
updateCheckedOptions(option, event) {
this.optionsMap[option] = event.target.checked;
}
3. to convert optionsMap into optionsChecked and store it in options before sending the POST request:
updateOptions() {
for(var x in this.optionsMap) {
if(this.optionsMap[x]) {
this.optionsChecked.push(x);
}
}
this.options = this.optionsChecked;
this.optionsChecked = [];
}
create a list like :-
this.xyzlist = [
{
id: 1,
value: 'option1'
},
{
id: 2,
value: 'option2'
}
];
Html :-
<div class="checkbox" *ngFor="let list of xyzlist">
<label>
<input formControlName="interestSectors" type="checkbox" value="{{list.id}}" (change)="onCheckboxChange(list,$event)">{{list.value}}</label>
</div>
then in it's component ts :-
onCheckboxChange(option, event) {
if(event.target.checked) {
this.checkedList.push(option.id);
} else {
for(var i=0 ; i < this.xyzlist.length; i++) {
if(this.checkedList[i] == option.id) {
this.checkedList.splice(i,1);
}
}
}
console.log(this.checkedList);
}
<input type="checkbox" name="options" value="option" (change)="updateChecked(option, $event)" />
export class MyComponent {
checked: boolean[] = [];
updateChecked(option, event) {
this.checked[option]=event; // or `event.target.value` not sure what this event looks like
}
}
I have encountered the same problem and now I have an answer I like more (may be you too). I have bounded each checkbox to an array index.
First I defined an Object like this:
SelectionStatusOfMutants: any = {};
Then the checkboxes are like this:
<input *ngFor="let Mutant of Mutants" type="checkbox"
[(ngModel)]="SelectionStatusOfMutants[Mutant.Id]" [value]="Mutant.Id" />
As you know objects in JS are arrays with arbitrary indices. So the result are being fetched so simple:
Count selected ones like this:
let count = 0;
Object.keys(SelectionStatusOfMutants).forEach((item, index) => {
if (SelectionStatusOfMutants[item])
count++;
});
And similar to that fetch selected ones like this:
let selecteds = Object.keys(SelectionStatusOfMutants).filter((item, index) => {
return SelectionStatusOfMutants[item];
});
You see?! Very simple very beautiful. TG.
Here's a solution without map, 'checked' properties and FormControl.
app.component.html:
<div *ngFor="let item of options">
<input type="checkbox"
(change)="onChange($event.target.checked, item)"
[checked]="checked(item)"
>
{{item}}
</div>
app.component.ts:
options = ["1", "2", "3", "4", "5"]
selected = ["1", "2", "5"]
// check if the item are selected
checked(item){
if(this.selected.indexOf(item) != -1){
return true;
}
}
// when checkbox change, add/remove the item from the array
onChange(checked, item){
if(checked){
this.selected.push(item);
} else {
this.selected.splice(this.selected.indexOf(item), 1)
}
}
DEMO
I hope this would help someone who has the same problem.
templet.html
<form [formGroup] = "myForm" (ngSubmit) = "confirmFlights(myForm.value)">
<ng-template ngFor [ngForOf]="flightList" let-flight let-i="index" >
<input type="checkbox" [value]="flight.id" formControlName="flightid"
(change)="flightids[i]=[$event.target.checked,$event.target.getAttribute('value')]" >
</ng-template>
</form>
component.ts
flightids array will have another arrays like this
[ [ true, 'id_1'], [ false, 'id_2'], [ true, 'id_3']...]
here true means user checked it, false means user checked then unchecked it.
The items that user have never checked will not be inserted to the array.
flightids = [];
confirmFlights(value){
//console.log(this.flightids);
let confirmList = [];
this.flightids.forEach(id => {
if(id[0]) // here, true means that user checked the item
confirmList.push(this.flightList.find(x => x.id === id[1]));
});
//console.log(confirmList);
}
In Angular 2+ to 9 using Typescript
Source Link
we can use an object to bind multiple Checkbox
checkboxesDataList = [
{
id: 'C001',
label: 'Photography',
isChecked: true
},
{
id: 'C002',
label: 'Writing',
isChecked: true
},
{
id: 'C003',
label: 'Painting',
isChecked: true
},
{
id: 'C004',
label: 'Knitting',
isChecked: false
},
{
id: 'C004',
label: 'Dancing',
isChecked: false
},
{
id: 'C005',
label: 'Gardening',
isChecked: true
},
{
id: 'C006',
label: 'Drawing',
isChecked: true
},
{
id: 'C007',
label: 'Gyming',
isChecked: false
},
{
id: 'C008',
label: 'Cooking',
isChecked: true
},
{
id: 'C009',
label: 'Scrapbooking',
isChecked: false
},
{
id: 'C010',
label: 'Origami',
isChecked: false
}
]
In HTML Template use
<ul class="checkbox-items">
<li *ngFor="let item of checkboxesDataList">
<input type="checkbox" [(ngModel)]="item.isChecked" (change)="changeSelection()">{{item.label}}
</li>
</ul>
To get selected checkboxes, add the following method in class
// Selected item
fetchSelectedItems() {
this.selectedItemsList = this.checkboxesDataList.filter((value, index) => {
return value.isChecked
});
}
// IDs of selected item
fetchCheckedIDs() {
this.checkedIDs = []
this.checkboxesDataList.forEach((value, index) => {
if (value.isChecked) {
this.checkedIDs.push(value.id);
}
});
}
I have just simplified little bit for those whose are using list of
value Object.
XYZ.Comonent.html
<div class="form-group">
<label for="options">Options :</label>
<div *ngFor="let option of xyzlist">
<label>
<input type="checkbox"
name="options"
value="{{option.Id}}"
(change)="onClicked(option, $event)"/>
{{option.Id}}-- {{option.checked}}
</label>
</div>
<button type="submit">Submit</button>
</div>
** XYZ.Component.ts**.
create a list -- xyzlist.
assign values, I am passing values from Java in this list.
Values are Int-Id, boolean -checked (Can Pass in Component.ts).
Now to get value in Componenet.ts.
xyzlist;//Just created a list
onClicked(option, event) {
console.log("event " + this.xyzlist.length);
console.log("event checked" + event.target.checked);
console.log("event checked" + event.target.value);
for (var i = 0; i < this.xyzlist.length; i++) {
console.log("test --- " + this.xyzlist[i].Id;
if (this.xyzlist[i].Id == event.target.value) {
this.xyzlist[i].checked = event.target.checked;
}
console.log("after update of checkbox" + this.xyzlist[i].checked);
}
I just faced this issue, and decided to make everything work with as less variables as i can, to keep workspace clean. Here is example of my code
<input type="checkbox" (change)="changeModel($event, modelArr, option.value)" [checked]="modelArr.includes(option.value)" />
Method, which called on change is pushing value in model, or removing it.
public changeModel(ev, list, val) {
if (ev.target.checked) {
list.push(val);
} else {
let i = list.indexOf(val);
list.splice(i, 1);
}
}
#ccwasden solution above works for me with a small change, each checkbox must have a unique name otherwise binding wont works
<div class="form-group">
<label for="options">Options:</label>
<div *ngFor="let option of options; let i = index">
<label>
<input type="checkbox"
name="options_{{i}}"
value="{{option.value}}"
[(ngModel)]="option.checked"/>
{{option.name}}
</label>
</div>
</div>
// my.component.ts
#Component({ moduleId:module.id, templateUrl:'my.component.html'})
export class MyComponent {
options = [
{name:'OptionA', value:'1', checked:true},
{name:'OptionB', value:'2', checked:false},
{name:'OptionC', value:'3', checked:true}
]
get selectedOptions() { // right now: ['1','3']
return this.options
.filter(opt => opt.checked)
.map(opt => opt.value)
}
}
and also make sur to import FormsModule in your main module
import { FormsModule } from '#angular/forms';
imports: [
FormsModule
],
Since I spent a long time solving a similar problem, I'm answering to share my experience.
My problem was the same, to know, getting many checkboxes value after a specified event has been triggered.
I tried a lot of solutions but for me the sexiest is using ViewChildren.
import { ViewChildren, QueryList } from '#angular/core';
/** Get handle on cmp tags in the template */
#ViewChildren('cmp') components: QueryList<any>;
ngAfterViewInit(){
// print array of CustomComponent objects
console.log(this.components.toArray());
}
Found here: https://stackoverflow.com/a/40165639/4775727
Potential other solutions for ref, there are a lot of similar topic, none of them purpose this solution...:
Angular 6: How to build a simple multiple checkbox to be checked/unchecked by the user?
Angular 6 How To Get Values From Multiple Checkboxes and Send in From
Angular how to get the multiple checkbox value?
Angular 2: Get Values of Multiple Checked Checkboxes
https://medium.com/#vladguleaev/reusable-angular-create-multiple-checkbox-group-component-84f0e4727677
https://netbasal.com/handling-multiple-checkboxes-in-angular-forms-57eb8e846d21
https://medium.com/#shlomiassaf/the-angular-template-variable-you-are-missing-return-of-the-var-6b573ec9fdc
https://www.bennadel.com/blog/3205-using-form-controls-without-formsmodule-or-ngmodel-in-angular-2-4-1.htm
How to get checkbox value in angular 5 app
Angular 2: Get Values of Multiple Checked Checkboxes
Filter by checkbox in angular 5
How to access multiple checkbox values in Angular 4/5