How to bind ngmodel inside each item in for loop angular 8? - checkbox

I want deselect the radio button, but after searching alot..I didn't find answer. So I choose the way of making checkbox work as a radio button(only single selection).And it worked fine but when I'm not able to bind ngmodel to checkbox.
I want the checkbox like
Item1
Id
Name
Item2
Id
Name
Item3
Id
Name
I need to select one checkbox item from the list of item (one from Id and Name).

Please check this code with use of [(ngModel)].
HTML
<div *ngFor = "let item of data ; let i = index">
<input type="checkbox" name="cb1" [(ngModel)]="item.IsSelected" (change)="toggleSelection(i)" /> {{item.name}}
</div>
Component
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
data : any = [
{'id': 1 , 'name': 'item-1'},
{'id': 2 , 'name': 'item-2'},
{'id': 3 , 'name': 'item-3'},
{'id': 4 , 'name': 'item-4'}
];
toggleSelection(i){
this.data.forEach((item, index) => {
if(index != i){
this.data[index].IsSelected = false
}
})
}
getSelectedItem(){
const selectedItem = this.data.filter( (item) => item.IsSelected );
console.log("selected option is ", selectedItem);
}
}

Related

Delete an object created when another checkbox is selected in the same row in angular

I have a simple table with descriptions and two columns representing a state: YES and NO.
Each checkbox represents an object in which it includes the "Condition" property that is null at startup. Depending on what is marked, an array of objects is formed. What I want to do is that when another checkbox in the same row is selected, delete the previously created object. Without affecting the other rows.
For example when I select a checkbox:
And by selecting the other checkbox, I would like to delete the previous object
I was playing around with the event checked and change to prevent the user from selecting the two checkboxes in the same row. Also to delete the created object by unchecking a selected checkbox, making the "Condition" true or false.
I have a demo on stackblitz: Demo
.HTML
<form [formGroup]="demoFormGroup" style="margin-top:20px; margin-bottom:30px">
<div formArrayName="info">
<table>
<tr>
<th></th>
<th>YES</th>
<th>NO</th>
</tr>
<tr *ngFor="let x of data; let i = index">
<td>{{x.Description}}</td>
<td>
<mat-checkbox
(change)="onChange($event,x,true)"
[checked]="x.Condition"
></mat-checkbox>
</td>
<td>
<mat-checkbox
(change)="onChange($event,x,false)"
[checked]="x.Condition != null && !x.Condition"
></mat-checkbox>
</td>
</tr>
</table>
</div>
</form>
<pre>{{ demoFormGroup.get('info')?.value | json }}</pre>
.TS
import { Component } from '#angular/core';
import { FormGroup, FormControl, FormArray, FormBuilder } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
demoFormGroup: FormGroup;
data: any;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.demoFormGroup = this.fb.group({
info: this.fb.array([])
});
this.data = [
{
Id: 4,
Description: 'Option 1',
Condition: null
},
{
Id: 6,
Description: 'Option 2',
Condition: null
}
];
}
onChange(event: any, option: any, enabled: boolean) {
option.Condition = enabled;
const ctrl = (<FormArray>this.demoFormGroup.get('info')) as FormArray;
if (event.checked) {
ctrl.push(new FormControl(option));
} else {
this.removeObject(ctrl, event);
}
}
removeObject(ctrl: any, event: any) {
const i = ctrl.controls.findIndex(
(x: any) => x.value === event.source.value
);
ctrl.removeAt(i);
}
}
There could be several options to achieve that the simplest approach could be get all the selected Ids and check that if the newly selected option Id is already exist then do not push to array.
i.e:
if (event.checked) {
// Get the form value
const selectedOptions = ctrl.value;
// Create a new array with only selected Ids
const selectedIds = selectedOptions.map(option => option.Id);
// Here before pushing to control array check if value does not exist.
if (!selectedIds.includes(option.Id)) {
ctrl.push(new FormControl(option));
}
} else {
this.removeObject(ctrl, event);
}

Angular cloned array gets changed automatically

I want to have a filter based on active-states from available checkboxes.
First everything should gets displayed, after a filter is selected, in this case a selection of hero names should only display heroes which contains atleast the name.
The interesting part is that: If I try to change the value back to the "full"-object it is not taking the complete object but an altered version of that.
I do not know what is happening there. Because I only initialized the full-object in the constructor of the app. With full-object I mean fullHeroes.
App.Component.ts :
import { Component, OnInit } from '#angular/core';
interface Hero {
name: string;
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
readonly fullHeroes: Hero[] = [];
heroes: Hero[] = [];
constructor() {
this.heroes.push(
{ name: 'Bob' }, { name: 'Alice' }, { name: 'Mallory' }
);
this.fullHeroes = this.heroes.slice(0);
}
filter(name, checked) {
if (checked) {
for (let i = 0; i <= this.heroes.length; i++) {
let found = false;
const currentHero = this.heroes[i];
if (currentHero && currentHero.name === name) {
found = true;
}
if (found) {
this.heroes.splice(i, 1);
}
}
} else {
this.heroes = [];
this.heroes = this.fullHeroes;
}
return;
}
}
App.component.html :
<div class="container">
<h1>World of Heroes</h1>
<p>Filter your Heroes based on names!</p>
<div class="filter">
<form #heroForm="ngForm">
<fieldset>
<legend>Choose the names</legend>
<div class="form-check" *ngFor="let hero of heroes">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" [name]="hero.name" (change)="filter($event.target.name, $event.target.checked)"> {{hero.name}}
</label>
</div>
</fieldset>
</form>
</div>
<hr>
<h2>Results:</h2>
<div class="row result-list">
<div class="col-md-4 hero" *ngFor="let hero of heroes">
<h3>Name: {{hero.name}}</h3>
</div>
</div>
</div>
If you want to make a clone for the array this.fullHeroes into the array this.heroes, use:
this.heroes = JSON.parse(JSON.stringify(this.fullHeroes));
This way you will do a full copy of the array this.fullHeroes into this.heroes one, and then changes in this.heroes will not affect the other array.
this.heroes = this.fullHeroes
After you execute that line, every time you mutate heroes, you also mutate fullHeroes, since they both refer to the same array. You need to make a copy of fullHeroes.
Note that you're abusing map() where you should be using forEach(). And you're using a loop where you could use some().
You can use the TypeScript spread operator to clone the array:
const a1 = [1, 2, 3, 4, 5, 6];
const a2 = [...a1];

indexOf comparing two lists, not matching exact values

Alright, so i have a list of checkmark forms, and upon clicking them, i submit an event and put all checked boxes values into a list, i then compare that list with another list to changes the matching checked values from true to false (using index of).
when i click canada, my console prints the expected output (Canada is true, United States and Mexico are false),
but then i click Mexico, so now Canada and Mexico have been selected, and all my countries turn to false.
i'm wondering if this is a problem with the way i'm implenting index of, or is it the order of calling functions that is causing this? a checked box should return true.
component:
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'app-geo-drop',
templateUrl: './geo-drop.component.html',
styleUrls: ['./geo-drop.component.css']
})
export class GeoDropComponent implements OnInit {
selectedItems: any [] = [];
places = [
{ code: 'US', allow: 'true', name: 'United States', continent: 'North America' },
{ code: 'CA', allow: 'true', name: 'Canada', continent: 'North America' },
{ code: 'MX', allow: 'false', name: 'Mexico', continent: 'North America' }
];
countriesForm: FormGroup;
constructor() { }
ngOnInit() {
// add checkbox for each country
this.countriesForm = new FormGroup({});
for (let i = 0; i < this.places.length; i++) {
this.countriesForm.addControl(
this.places[i].name, new FormControl(false)
)
}
}
allow(selectedPlaces: Array<any>, allPlaces: Array<any>) {
allPlaces.filter(function (place) {
if (place.name.indexOf(selectedPlaces) > -1) {
place.allow = true;
} else {
place.allow = false;
};
});
this.places.forEach(item => {
console.log(item.name, item.allow);
});
}
search(place, event) {
let index = this.selectedItems.indexOf(place.name);
if (event.target.checked) {
if (index === -1) {
this.selectedItems.push(place.name);
console.log(this.selectedItems);
}
} else {
if (index !== -1) {
this.selectedItems.splice(index, 1);
console.log(this.selectedItems);
}
}
this.allow(this.selectedItems, this.places);
}
}
html
<div class="geo-list">
<div class="content-box container">
<form [formGroup]="countriesForm">
<div class="place" *ngFor="let place of places">
<input
type="checkbox"
formControlName="{{place.name}}"
(change)="search(place, $event)"
>
{{ place.name }} | {{ place.code }} | {{ place.continent }}
</div>
</form>
</div>
</div>
The answer is in your own debug info; you're asking the index of "Canada, Mexico"
Since none of your countries are called "Canada, Mexico", they're false.
You need to loop through all selected boxes in your html to fix it.
I found a simple solution. though i don't believe it's the fastest, if a faster method is known, please post it. this method changes the value directly on every (change), and then binds the checkbox to the current value of allowed
comp.
export class GeoDropComponent implements OnInit {
places = [
{ code: 'US', allow: true, name: 'United States', continent: 'North America' },
{ code: 'CA', allow: true, name: 'Canada', continent: 'North America' },
{ code: 'MX', allow: false, name: 'Mexico', continent: 'North America' }
];
countriesForm: FormGroup;
constructor() { }
// use ng on destroy to send final copy?
ngOnInit() {
// add checkbox for each country
this.countriesForm = new FormGroup({});
for (let i = 0; i < this.places.length; i++) {
this.countriesForm.addControl(
this.places[i].name, new FormControl(false)
)
}
}
search(place, event) {
for (let i = 0; i < this.places.length; i++) {
if (this.places[i].name === place.name) {
this.places[i].allow === true ? this.places[i].allow = false : this.places[i].allow = true;
}
}
console.log(place.allow);
console.log(this.places);
}
}
html
<input
type="checkbox"
formControlName="{{place.name}}"
value="{{place.allow}}"
(change)="search(place, $event)"
[checked]="place.allow"
>

Using a pipe to filter an array of objects by an array of strings in Angular2

I am attempting to filter an array of objects which have a similar structure to this:
posts = [
{
title: 'post 1 title',
location: 'Location 1'
},
{
title: 'post 2 title',
location: 'Location 2'
},
{
title: 'post 3 title',
location: 'Location 3'
}
]
I am filtering this array of objects (above) with an array of strings (below)
locations = [
'Location 1',
'Location 2',
'Location 3'
]
How it should be working:I enter into a 'tag' style input box in the browser as many locations as I want and it should display the posts with location properties matching those location 'tags' in the locations array.
All the posts are initially displayed (which is good), then when I enter tags I am able to get the filtered objects in the log which shows that they seem to be getting filtered based on my inputs. but the html in which the posts are looped through does not show the relevant posts.
I am using firebase to store the posts, and calling my service which returns all the posts on ngOnInit()
ngOnInit() {
this._tags = [];
this._postService.getAllPosts().subscribe(posts => {
this._posts = posts;
});
}
HTML:
<!--input *edited some stuff out like the add tag function. 'term' model gets pushed into the _tags array on enter or click etc-->
<input type="text" placeholder="Search" [(ngModel)]="term [ngModelOptions]="{standalone: true}">
<!--tag list, shows tags that have been searched-->
<div class="tag" *ngFor="let t of _tags">{{t}}</div>
<!filtered post list-->
<div *ngFor="let post of _posts | tagsearchfilter:_tags">
<h4>{{post.title}}</h4>
</div>
...
Pipe filter:
import { Pipe, PipeTransform } from '#angular/core';
import { PostService } from '../../services/post/post.service';
#Pipe({
name: 'tagsearchfilter'
})
export class TagSearchFilterPipe implements PipeTransform {
transform(posts: any, tags: any): any {
let rposts = [];
if (tags === undefined || tags.length === 0 ) {
return posts;
}
posts = posts.filter(function(post){
tags.forEach(tag => {
if (
post.location.toLowerCase().includes(tag.toLowerCase())
) {
rposts.push(post);
console.log('Post location: ' + post.location + ' Tag: ' + tag);
}
});
posts = rposts;
console.log(posts);
return posts;
});
}
}

Two comboboxes showing nested arrays in AngularJS

I have an array which as arrays as members:
[
{"group":"Canada","data":[{"value":"Ontario","description":"Toronto"},
{"value":"Quebec","description":"Quebec City"},
{"value":"Manitoba","description":"Winnipeg"}
]
},
{"group":"Mexico","data":[{"value":"Jalisco","description":"Guadalajara"},
{"value":"Nayarit","description":"Tepic"}
]
},
{"group":"United States of America","data":[
{"value":"Alabama","description":"Montgomery"},
{"value":"Georgia","description":"Atlanta"},
{"value":"Mississippi","description":"Jackson"},
{"value":"Louisiana","description":"Baton Rouge"},
{"value":"Texas","description":"Ausint"}
]
}
]
I would like to have an AngularJS combobox which shows the list of group countries. When users select each country, I would like another combo to show the names of regions in that country.
Can anybody point me to tutorials which show how to achieve it?
Plunker example
#Component({
selector: 'my-app',
providers: [],
template: `
<div>
<select [(ngModel)]="selectedGroup">
<option *ngFor="let group of groups" [ngValue]="group">{{group.group}}</option>
</select>
<select [(ngModel)]="selectedData">
<option *ngFor="let item of getData()" [ngValue]="item">{{item.value}}</option>
</select>
</div>
`,
directives: []
})
export class App {
selectedGroup;
selectedData;
groups = [
...
];
constructor() {
this.name = 'Angular2 (Release Candidate!)'
}
getData() {
let idx = this.groups.indexOf(this.selectedGroup);
if(idx >= 0) {
console.log(this.groups[idx]);
return this.groups[idx].data;
}
}
}

Resources