ngClass active button function Angular - html

How can I change the style of each button once I click on it?
I have several buttons and I need to achieve something similar to an active state, but in reality what you execute is a function.
I tried the following but I do not achieve an active state [ngClass]=" { 'btn-primary':categoria.nombre }
component.ts
ngOnInit() {
this.eventos = this.fs.getEventosAll();
this.categorias = this.fs.getCategorias();
}
filtrarEventos(categoria) {
this.eventos = this.fs.filterEventos(categoria);
}
component.html
<button class="btn btn-sm" *ngFor="let categoria of categorias | async" (click)="filtrarEventos(categoria.nombre)" [ngClass]=" { 'btn-primary':categoria.nombre } ">
{{ categoria.nombre }}
</button>

You can first, add a property, let's say, categoriaActiva to the component holding the buttons, add
this.categoriaActiva = categoria
to the filtrarEventos callback, and finally add
[class.btn-primary]="categoria.nombre === categoriaActiva"
to the button.

Here is the stackblitz of what you are trying to do
I called one method which sets the value of the clicked index
Template
<div *ngFor="let categoria of categorias; let i = index"
(click)="changeState(i)"
[ngClass]="clickedIndex === i ? 'primary' : 'secondary'">
{{categoria.nombre}}
</div>
Component
changeState(index) {
this.clickedIndex = index;
}

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

Add Data Dynamically to HTML Button in HTML page

I have this button, I need to add Pendo Data that is dynamically working based on which button we chose. Mostly this is making the Button unique. When I have a button that is not changing I add like this:
<button mat-button
 data-pendo="pendo-prospects-send-application"
class='round-button'
color='primary'
type='button'
.....>
</button>
But sometime I need to add this data to one button that is changing based on CSS class. I am not sure how check for that.
For example I need to add to a button when :
if [class.fa-pencil] then data-pendo "Something"
if [class.fa-plus] then data-pendo "Something else"
This is the button that changes base on class:
<button mat-button
class='round-button'
type='button'
[class.disabled-button]='GuidId'
color='primary'
(click)='onAssignLoanOfficer()'>
<i class='fal'
[class.fa-pencil]='GuidId'
[class.fa-plus]='!GuidId'></i>
</button>
How I can do that?
Based on your comments I think this is what you want to do:
<button
(click)="onAssignLoanOfficer()"
[class.disabled-button]="GuidId"
[attr.data-pendo]="GuidId ? 'pendo-edit-loan' : 'pendo-add-loan'"
class="round-button"
color="primary"
type="button">
<i [class.fa-pencil]="GuidId"
[class.fa-plus]="!GuidId"
class="fall">
</i>
</button>
ALTERNATIVE:
As a more sophisticated alternative (I really don't know how you intend to use what you're asking for), you can build a directive to add the attribute you want based on a map of class-to-pendo-data conversion information:
#Directive({
selector: '[addPendoData]'
})
export class AddPendoDataDirective implements AfterViewInit {
constructor(private _el: ElementRef, private _renderer: Renderer2) {}
ngAfterViewInit() {
const pendoData: string | null | undefined = this._getPendoValue();
if (!pendoData) { return;}
const $button: HTMLElement = this._el.nativeElement;
this._renderer.setAttribute($button, 'data-pendo', pendoData);
}
private _getPendoValue() {
const $child: HTMLElement = this._el.nativeElement;
if(!$child) { return null; }
const $i: HTMLElement = $child.querySelector('i');
if(!$i) { return null; }
const listOfClasses: string[] = $i.className.split(' ');
if (!(listOfClasses && listOfClasses.length)) { return null; }
for(const className of listOfClasses) {
if(PENDO_MAP[className]) { return PENDO_MAP[className]; }
}
return null;
}
}
const PENDO_MAP: { [className: string]: string } = {
'fa-pencil': 'pendo-edit-loan',
'fa-plus': 'pendo-add-loan'
// add other mappings here...
};
and you can use it like this:
<button
(click)="onAssignLoanOfficer()"
[class.disabled-button]="GuidId"
addPendoData
class="round-button"
color="primary"
type="button">
<i [class.fa-pencil]="GuidId"
[class.fa-plus]="!GuidId"
class="fall">
</i>
</button>
I've put together this stackblitz demo.
You could define a directive, as julianobrasil suggests.
I want to point a different way to achieve this using Angular templates.
Define two buttons, addition and edition, separately.
Put a default where you want to be rendered
<div class="action-button" *ngIf="GuidId">
<!-- Add button -->
<button mat-button
data-pendo="pendo-add-loan"
(click)="add(...)"
class="round-button"
color="primary">
<i class="fal fa-plus"></i>
</button>
</div>
Then, define a ng-template tag with the other button definition. A completely fresh new.
<ng-template #edit-button>
<div class="action-button">
<!-- Edit button -->
<button mat-button
(click)="edit(...)"
data-pendo="pendo-edit-loan"
class="round-button"
color="primary">
<i class="fal fa-pencil"></i>
</button>
</div>
</ng-template>
Finally, just change the *ngIf statement to render either an addition button or an edition one.
<div class="action-button" *ngIf="!GuidId else edit-button">
<!-- Add button here -->
</div>
This is a way that scales in order to keep components isolated. The buttons are not related anymore, so you can implement w/o having to condition every style, action, etc.
Hope it helps.

How to access a dynamically generated id of a HTML element with angular

<div *ngFor="let link of links; let i = index;">
<p>{{link.full}}</p>
<button [id]='i' (click)="copyToClipboard(copy) ">Copy</button>
</div>
How do I access the [id]="i" in my typescript file
Have a look at this demo:
[https://stackblitz.com/edit/angular-irxzeg?file=src%2Fapp%2Fapp.component.ts]
It includes suggestions from #Sam Herrmann
And with Renderer, to add Class
[https://stackblitz.com/edit/angular-mjvvp1?file=src%2Fapp%2Fapp.component.ts]
You don't need to add an id or to change 'manually' the label of button.
You can add a flag to know if link has been copied or not, inside your existing model.
For example:
export type Link = {
full: string,
copied: boolean
}
Then, inside your code you can set this flag to true:
copyToClipboard(link: Link) {
link.copied = true;
...
}
In template, just use this flag to adapt the label of button, and also to set a specific class on button (or elsewhere):
<div *ngFor="let link of links">
<p>{{link.full}}</p>
<button (click)="copyToClipboard(link)" [class.copied]="link.copied">
{{ link.copied ? 'Copied' : 'Copy' }}</button>
</div>
.copied {
background-color: green
}

Switch / Toggle dynamically created components

I have two child component in parent component which dynamically created.
My goal is to switch/toggle between this components.
<div *ngFor="let device of devices; let i = index">
<app-standart-view [value]="device.value" (click)="SWICH TO DETAIL-VIEW"></app-standart-view>
<app-detail-view [value]="device.value" (click)="SWICH TO STD-VIEW"></app-detail-view>
</div>
Scenario:
When I click <app-standart-view> it hides itself and displays <app-detail-view>. But only for that clicked component index = i. The rest should don't change.
How can I do this?
Thank you
you can use *ngIf In ts file you will have to define a variable
hide:boolean =true
switchView(){
this.hide=!this.hide
}
and in html
<app-standart-view [value]="device.value" *ngIf= "hide" (click)="switchView()"></app-standart-view>
<app-detail-view [value]="device.value" *ngIf= "!hide" (click)="switchView()"></app-detail-view>
i'd use an *ngIf='device.toShow' directiv in your <app-detail-view>
And obviously setting the proper value of toShow in your methode that you use ater a click event on <app-standart-view>
<div *ngFor="let device of devices; let i = index">
<app-standart-view [value]="device.value" *ngIf="show" (click)="swichView(i)"></app-standart-view>
<app-detail-view [value]="device.value" *ngIf="!show" (click)="swichView(i)"></app-detail-view>
</div>
For specific index you need to pass index and check index
show:boolean = true;
swichView(index:number){
if(index === 1 ){
this.show = !this.show;
}
}

How to stop property binding to every element in Angular within *ngFor

In my html, I want to apply property binding to every element.
I have a click and hover event that I want to do whenever the user
hovers or clicks on an individual element. But right now the hover or
click happens to every element within the *ngFor. I want it to only
happen on the element they are selecting/hovering over. What do I need
to change?
I saw another stackoverflow answer and they simply applied the name
within the for loop (ex: *ngFor="let article of articles" and they
used article) in front of the boolean/variable they were setting.
Like my boolean is favorite so they did article.favorite within
the element and it apparently worked, but that method doesn't work for
me.
Code:
<div class="row">
<!--/span-->
<div class="col-6 col-sm-6 col-lg-4"
*ngFor="let article of articles">
<h2>{{article.title}}</h2>
<h4>By: {{article.author}}</h4>
<p>{{article.body}}</p>
<div class="col-lg-4">
<button type="button" class="btn btn-default" (click)="addFavorite()"
(mouseenter)="hoverFavorite()"
(mouseleave)="removeHoverFavorite()">
<span
class="glyphicon"
[class.glyphicon-heart]="favorite"
[class.glyphicon-heart-empty]="!favorite"
aria-hidden="true"></span> Favorite
</button>
</div>
<div class="col-lg-4">
<button type="button" class="btn btn-default">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Comment
</button>
</div>
<div class="col-lg-4">
<button type="button" class="btn btn-info pull-left" [routerLink]="['/articles', article.articleId]">Read More »
</button>
</div>
</div>
</div>
<!--/row-->
Adding component
import { Component, OnInit } from '#angular/core';
import {ArticlesService} from "./articles.service";
import {Article} from "./article.model";
import {Router} from "#angular/router";
#Component({
selector: 'app-articles',
templateUrl: './articles.component.html',
styleUrls: ['./articles.component.css']
})
export class ArticlesComponent implements OnInit {
articles: Article[];
// if favorite = false then full heart is not shown. if true then heart shown
favorite: boolean = false;
// clicked will be used to determine if we should keep hovering effect on
clicked: boolean = false;
constructor(private router: Router, private articleService: ArticlesService) { }
ngOnInit() {
this.articleService.getArticles()
.subscribe(
(articles: Article[]) => {
this.articles = articles;
}
);
}
addFavorite(){
// toggle full and empty heart
this.clicked = !this.clicked;
if (this.clicked === true){
// if clicked then add to database and show full heart
this.favorite = true;
} else { // if false then remove from database and show empty heart
this.favorite = false;
}
}
hoverFavorite(){
// if clicked is false then show hover effect, else dont
if (this.clicked === false){
this.favorite = true;
}
}
removeHoverFavorite(){
// if clicked is false then show hover effect, else dont
if (this.clicked === false){
this.favorite = this.favorite = false;
}
}
}
You can use the index
*ngFor="let article of articles; let i=index"
(click)="addFavorite(i)"
// or
(click)="addFavorite(article)"
(mouseenter)="hoverFavorite = i"
(mouseleave)="hoverFavorite = -1"
[class.glyphicon-heart]="favorite === i"
[class.glyphicon-heart-empty]="favorite !== i"
The reason you're not getting the desired result is because defined your boolean variables in the ArticlesComponent, which is the component rendering the list of articles. Thus, if the variables become true, it would be true for all the articles, instead of a single one. To fix it you should define all of the content within the ngFor loop as its own component, and in that component you would set those boolean variables. That way, each instance of an ArticleComponent would have its own variables and they wouldn't interfere with each other.