Add and Remove class in Angular - html

I want to add background color to the li element when clicked but when I clicked another li element the previous li element background color remains unchanged.
component.html
<div class="col-md-3 categories">
<h3>News By Type</h3>
<ul>
<li class="cat" id="cat_{{i}}" *ngFor="let category of categories; let i = index" (click)="sortNewsItems($event,category,i)"><img src="../assets/images/news.jpg" width="70"><h4>{{category}}</h4></li>
</ul>
</div>
component.ts
sortNewsItems(event,cat,index) {
event.target.classList.add('cat_active');
}

You should use srcElement of the $event
sortNewsItems(event,cat,index) {
event.srcElement.classList.add('cat_active');
}
Read this answer and use its demo

I know this is an old post but just in case. when there are several classes already on the element you might just want to add or remove an extra class you can do this:
On the element:
<div #element </div>
On the component.ts
#ViewChild('element') element: ElementRef;
then you can just add classes or remove them by
this.element.nativeElement.classList.add("newclass");
this.element.nativeElement.classList.remove("newclass");

Remove 'cat_active' class from all the sibling elements before adding a new 'cat_active' class to the selected element.
component.html
<li #li class="cat" *ngFor="let category of categories;" (click)="sortNewsItems($event)">
component.ts
#ViewChildren('li') livs: QueryList<any>;
constructor(private elementRef: ElementRef) { }
sortNewsItems(event) {
this.livs.forEach(liv => liv.nativeElement.children[0].classList = []);
event.target.classList.add('cat_active');
}
I hope it might helps.

Use ngStyle instead of direct class binding in html element.
component.html
<div class="col-md-3 categories">
<h3>News By Type</h3>
<ul>
<li [ngStyle]="setListItemStyle(category)" class="cat" id="cat_{{i}}" *ngFor="let category of categories; let i = index" (click)="sortNewsItems($event,category,i)"><img src="../assets/images/news.jpg" width="70"><h4>{{category.label}}</h4></li>
</ul>
</div>
component.ts
activeListItem: any = null;
categories: any[] = [{ id: 1, label: 'Test label 1' }, { id: 2, label: 'Test label 2' }];
sortNewsItems(event, category, i) {
event.stopPropagation();
this.activeListItem = category;
}
setListItemStyle(category) {
return { 'background-color': this.activeListItem && this.activeListItem.id == category.id ? '#fff000' : null };
}

I just taken a variable and set category name to it when clicked on category li and add active class based on the below condition. finally I set it like what i want. Thank you everyone for the well support.
component.html
<li class="cat" *ngFor="let category of categories; let i = index" (click)="sortNewsItems(category,i)" [ngClass]="{'cat_active':toggleClass === category}"><img src="../assets/images/news.jpg" width="70"><h4>{{category}}</h4></li>
component.ts
toggleClass:string;
sortNewsItems(cat,index) {
this.toggleClass = cat;
}

I read that using srcElement is not a "so good" practice. Better to use renderer2.

<any-element [ngClass]="{selected: isSelected}">
...
</any-element>
OR
<any-element [ngClass]="{selected: isSelected, disabled: isDisabled}">
...
</any-element>

document.querySelector(".menu-open-btn a").onclick = function addNewClass() {
document.querySelector(".mobile-header").classList.add("newClass");
}

Related

How to change the text of the currently selected button inside an ngFor?

I have an ngFor loop going on some membership options. I would like to change the text inside the button from select to selected once the user chooses their options. The code i have now unfortunately changes all three buttons to selected when one has been clicked.
Typescript:
export class SelectPlanComponent implements OnInit {
plans: any;
selectedPlan?: Plan;
buttonValue = "Select";
constructor(private service: PlansService) { }
ngOnInit(): void {
this.service.getPlans()
.subscribe((response) => {
this.plans = response;
console.log(response);
});
}
onSelect(plan: Plan): void {
this.selectedPlan = plan;
this.buttonValue = "Selected"
}
}
HTML:
<div class="card" *ngFor="let plan of plans">
<h2>{{plan.title}}</h2>
<p >£{{plan.amount}}</p>
<p>{{plan.duration}}</p>
<div class="benefits-container">
<div class="benefits" *ngFor="let item of plan.description.items">
<div class="icon-container">
<mat-icon>check</mat-icon>
</div>
<p>{{item}}</p>
</div>
</div>
<a class="inverted-button" [class.selected]="plan === selectedPlan" (click)="onSelect(plan)">{{buttonValue}}</a>
</div>
Thank you in advance
You should create a property on the Plan object to keep track of whether it has been selected or not. Do this by modifying your Plan class to include a selected property, like this:
export class Plan {
// existing properties go here
selected: boolean;
}
Then, in your onSelect() method, you can set the selected property on the plan object to true when it is selected, like this:
onSelect(plan: Plan): void {
this.selectedPlan = plan;
plan.selected = true;
this.buttonValue = "Selected";
}
In your HTML, you can use this selected property to conditionally render the "Selected" text on the button, like this:
<a class="inverted-button" [class.selected]="plan === selectedPlan" (click)="onSelect(plan)">
{{ plan.selected ? "Selected" : "Select" }}
</a>
You currently have one variable which is bound to all buttons. What you could do is the following
Checking if the selectedPlan is the plan for the current button
<div class="card" *ngFor="let plan of plans">
<h2>{{plan.title}}</h2>
<p >£{{plan.amount}}</p>
<p>{{plan.duration}}</p>
<div class="benefits-container">
<div class="benefits" *ngFor="let item of plan.description.items">
<div class="icon-container">
<mat-icon>check</mat-icon>
</div>
<p>{{item}}</p>
</div>
</div>
<a class="inverted-button" [class.selected]="plan === selectedPlan" (click)="onSelect(plan)">{{plan === selectedPlan ? 'selected' : 'select' }}</a>
</div>
and after that you can change your TypeScript Code as follows:
onSelect(plan: Plan): void {
this.selectedPlan = plan;
}

Push the checked/selected item of checkbox at the top of the list in Angular9 and above

I am trying to get the selected checkbox on the top the list Say If I
selected fourth checkbox among five checkboxes(12345) the result
should be (41235) please help for your reference I have added work
done till now and add link also.
TS file
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
options = ['1', '2', '3', '4', '5', '6'];
selected = [];
messages = [];
// 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);
}
console.log(this.selected);
}
save() {
this.messages.push(this.selected.sort());
}
}
*** HTML File ***
<h1>Angular Checkbox List</h1>
<h3>Options</h3>
{{options | json}}
<h3>Selected</h3>
{{selected | json}}
<br>
<h3>List</h3>
<div *ngFor="let item of options">
<input type="checkbox"
(change)="onChange($event.target.checked, item)"
[checked]="checked(item)"
>
{{item}}
</div>
<br>
{{selected.length}} items selected <br>
<button (click)="save()">Save</button>
<h3 *ngIf="messages.length != 0">Log</h3>
<div *ngFor="let item of messages">
save: {{item}}
</div>
list given
[] one (say I selected first this checkbox)
[] two
[] three (second this checkbox)
[] four
[] five (next this checkbox)
excepted resulted
[]five
[]three
[]one
[]two
[]four
working here stackblitz
Add the index to the *ngFor in your HTML file.
<div *ngFor="let i = index; let item of options">
<input type="checkbox"
(change)="onChange($event.target.checked, item, i)"
[checked]="checked(item)">
{{item}}
</div>
So you can use it as parameter in your onChange function. That way you can remove from that index and push it to the top as this:
// when checkbox change, add/remove the item from the array
onChange(checked, item, index) {
if (checked) {
// Removes it from the index
this.options.splice(index, 1);
// Push it in the first position
this.options.unshift(item);
this.selected.push(item);
} else {
this.selected.splice(this.selected.indexOf(item), 1);
}
console.log(this.selected);
}
Here I modified your Stackbliz code.
Snapshot of the result
If you want the item to return to its original index when it is deselected:
// when checkbox change, add/remove the item from the array
onChange(checked, item, index) {
if (checked) {
// Removes it from the index
this.options.splice(index, 1);
// Push it in the first position
this.options.unshift(item);
this.selected.push(item);
} else {
// Removes it from the index
this.options.splice(index, 1);
// Place it in the original index
this.options.splice(this.optionsCopy.indexOf(item), 0, item);
this.selected.splice(this.selected.indexOf(item), 1);
}
console.log(this.selected);
}
Here the working Stackblitz.
Update:
Add these two lines in onChange method
this.options.splice(this.options.indexOf(item), 1);
this.options.unshift(item);
Note: Stackblitz link is updated.
Old:
Just remove .sort() method from the line this.messages.push(this.selected.sort());. It will give the exactly selected items in the order you selected them.
I have modified it in your Stackblitz link- https://stackblitz.com/edit/angular-wmimex

change <li> style by click

I am using Angular CLI. Actually, I have a menu list. And I would like to change the background color of <li> when I click on it.
I send id to functionchangeColor(). But the problem is I am getting menuButton as null. Please help me to do so.
.html
<ul>
<li id="menu_btn" (click)="changeColor()" >
</li>
<li id="menu_btn" (click)="changeColor()">
</li>
<ul>
.ts:
changeColor() {
let menuButton = document.getElementById("menu_btn");
menuButton.style.backgroundColor = '#816587';
}
The more angular way to do this is define your menu list in an array
// component.ts
menuList = [
{
name: 'Option 1',
isSelected: false
},
{
name: 'Option 2',
isSelected: false
}
];
Then use an ngFor to render the list and use ngClass + each menu items isSelected flag to toggle a selected class.
// component.html
<ul>
<li *ngFor="let menuItem of menuList" (click)="changeColor(menuItem)"
[ngClass]="{'selected': menuItem.isSelected}">
{{menuItem.name}}
</li>
</ul>
// component.css
.selected{
background-color: #816587;
}
Now have your changeColor method handle the toggling of the isSelected flag
changeColor(menuItem){
// toggle off all selected menu items
this.menuList.forEach(item => {
if(item.isSelected){
item.isSelected = false;
}
});
// select clicked menu item
menuItem.isSelected = true;
}
This way angular is handling all of the DOM manipulation. Here is plnkr demonstrating this functionality (https://plnkr.co/edit/NEnzKZ84M85mErYGLE1Y?p=preview)
You can use ngStyle to do this:
<li id="menu_btn" (click)="myStyle = {'background-color': #816587}" [ngStyle]="myStyle">

add div of two select tags on a button in angular?

I am new to angular. I am using typescript.
I want to achieve this task.
Here is the image of what I want do.
I want to add the date and location selector on add button click. I actually did that but I am unable to maintain its data when I remove its row.
Here is what I did in html:
<div id="" *ngFor = "let sp of specialityElementCount">
<select id="specialityDate"
class="textField"(change)='selectSpecialityDate($event.target.value)'>
<option>Select Date</option>
<option *ngFor = "let date of spDates"
[value]="date">{{date}}</option>
</select>
<select class="textField"
[ngClass]="{'specialityLocation':(sp=='speciality1'),'specialityLocationRemove':(sp!='speciality1')}"
(change)='selectSpecialityLocation($event.target.value, sp)'>
<option>Select Location</option>
<option *ngFor = "let location of specialityLocation"
[value]="location">{{location}}</option>
</select>
<div class= "removeImgDiv"[hidden]="sp=='speciality1'">
<img class="removeImg" (click)="removeRow(sp)" src="../../assets/img/remove.png">
</div>
</div>
and here is what I did in typescript:
specialityElementCount = ["speciality1"];
addRow(row:string){
console.log(row);
if(row.includes("speciality")){
this.specialityElementCount.push("speciality"+(this.specialityElementCount.length+1));
}
}
removeRow(row){
console.log(row);
if(row.includes("speciality")){
this.specialityElementCount.splice(this.tripLocations.indexOf(row), 1);
}
}
selectSpecialityDate(event){
console.log(event);
}
selectSpecialityLocation(event, text){
console.log(event);
console.log(text);
}
Please help me with this. That when I remove row it removes the correct row instead of remove the last row every time.
Thanks in advance.
What you can do use the index generated by *ngFor instead of searching for the row yourself.
It would look something like this:
#Component({
selector: 'my-app',
template: `
<div>
<div *ngFor="let row of data; let index = $index">
<div>{{row}}</div>
<div (click)="removeRow(index)">X</div>
</div>
</div>
`,
})
export class App {
data:string[];
constructor() {
this.data = ['a', 'b','c']
}
removeRow(index) {
this.data.splice(index, 1);
}
}
You can see a working plunkr with the example here.

How to setup the class in my case

I am trying to add the class by ng-class in my app.
I have something like
html
<li ng-repeat="item in items"
ng-click="getItem(item)"
ng-class="{shine : item.isPick}" // shine class will make the font color change to red
>{{item.name}}
</li>
In my controller
$scope.getItem = function(item) {
$scope.items.forEach(function(item){
item.isPick = false; // remove all the items that have shine class
})
item.isPick = true; // adding the shine class back
}
Basically I want to remove every other 'shine' class except the selected item when user clicks it. My codes above work but I am thinking there is a better way to do it instead of looping all the items. Can someone help me about it? Thanks a lot!
Use the $index in the repeater, and add the class when that index is selected:
<li ng-repeat="item in items"
ng-click="getItem($index)"
ng-class="{shine : activeIndex == $index}"
>{{item.name}}
</li>
$scope.getItem = function(index) {
$scope.activeIndex = index;
}