Appending additional classes to HTML elements based on condition in Angular [duplicate] - html

What is wrong with my Angular code? I am getting the following error:
Cannot read property 'remove' of undefined at BrowserDomAdapter.removeClass
<ol>
<li *ngClass="{active: step==='step1'}" (click)="step='step1'">Step1</li>
<li *ngClass="{active: step==='step2'}" (click)="step='step2'">Step2</li>
<li *ngClass="{active: step==='step3'}" (click)="step='step3'">Step3</li>
</ol>

Angular version 2+ provides several ways to add classes conditionally:
type one
[class.my_class] = "step === 'step1'"
type two
[ngClass]="{'my_class': step === 'step1'}"
and multiple option:
[ngClass]="{'my_class': step === 'step1', 'my_class2' : step === 'step2' }"
type three
[ngClass]="{1 : 'my_class1', 2 : 'my_class2', 3 : 'my_class4'}[step]"
type four
[ngClass]="step == 'step1' ? 'my_class1' : 'my_class2'"
You can find these examples on the documentation page

[ngClass]=... instead of *ngClass.
* is only for the shorthand syntax for structural directives where you can for example use
<div *ngFor="let item of items">{{item}}</div>
instead of the longer equivalent version
<template ngFor let-item [ngForOf]="items">
<div>{{item}}</div>
</template>
See also https://angular.io/docs/ts/latest/api/common/index/NgClass-directive.html
<some-element [ngClass]="'first second'">...</some-element>
<some-element [ngClass]="['first', 'second']">...</some-element>
<some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
<some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
<some-element [ngClass]="{'class1 class2 class3' : true}">...</some-element>
See also https://angular.io/docs/ts/latest/guide/template-syntax.html
<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>
<!-- binding to `class.special` trumps the class attribute -->
<div class="special"
[class.special]="!isSpecial">This one is not so special</div>
<!-- reset/override all class names with a binding -->
<div class="bad curly special"
[class]="badCurly">Bad curly</div>

Another solution would be using [class.active].
Example :
<ol class="breadcrumb">
<li [class.active]="step=='step1'" (click)="step='step1'">Step1</li>
</ol>

That's the normal structure for ngClass is:
[ngClass]="{'classname' : condition}"
So in your case, just use it like this...
<ol class="breadcrumb">
<li [ngClass]="{'active': step==='step1'}" (click)="step='step1'">Step1</li>
<li [ngClass]="{'active': step==='step2'}" (click)="step='step2'">Step2</li>
<li [ngClass]="{'active': step==='step3'}" (click)="step='step3'">Step3</li>
</ol>

with the following examples you can use 'IF ELSE'
<p class="{{condition ? 'checkedClass' : 'uncheckedClass'}}">
<p [ngClass]="condition ? 'checkedClass' : 'uncheckedClass'">
<p [ngClass]="[condition ? 'checkedClass' : 'uncheckedClass']">

You can use ngClass to apply the class name both conditionally and not in Angular
For Example
[ngClass]="'someClass'">
Conditional
[ngClass]="{'someClass': property1.isValid}">
Multiple Condition
[ngClass]="{'someClass': property1.isValid && property2.isValid}">
Method expression
[ngClass]="getSomeClass()"
This method will inside of your component
getSomeClass(){
const isValid=this.property1 && this.property2;
return {someClass1:isValid , someClass2:isValid};
}

Angular provides multiple ways to add classes conditionally:
First way
active is your class name
[class.active]="step === 'step1'"
Second way
active is your class name
[ngClass]="{'active': step=='step1'}"
Third way
by using ternary operator class1 and class2 is your class name
[ngClass]="(step=='step1')?'class1':'class2'"

You should use something ([ngClass] instead of *ngClass) like that:
<ol class="breadcrumb">
<li [ngClass]="{active: step==='step1'}" (click)="step='step1; '">Step1</li>
(...)

In Angular 7.X
The CSS classes are updated as follows, depending on the type of the expression evaluation:
string - the CSS classes listed in the string (space delimited) are added
Array - the CSS classes declared as Array elements are added
Object - keys are CSS classes that get added when the expression given in the value evaluates to a truthy value, otherwise they are removed.
<some-element [ngClass]="'first second'">...</some-element>
<some-element [ngClass]="['first', 'second']">...</some-element>
<some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
<some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
<some-element [ngClass]="{'class1 class2 class3' : true}">...</some-element>

Additionally, you can add with method function:
In HTML
<div [ngClass]="setClasses()">...</div>
In component.ts
// Set Dynamic Classes
setClasses() {
let classes = {
constantClass: true,
'conditional-class': this.item.id === 1
}
return classes;
}

to extend MostafaMashayekhi his answer for option two>
you can also chain multiple options with a ','
[ngClass]="{'my-class': step=='step1', 'my-class2':step=='step2' }"
Also *ngIf can be used in some of these situations usually combined with a *ngFor
class="mats p" *ngIf="mat=='painted'"

You can use [ngClass] or [class.classname], both will work the same.
[class.my-class]="step==='step1'"
OR
[ngClass]="{'my-class': step=='step1'}"
Both will work the same!

While I was creating a reactive form, I had to assign 2 types of class on the button. This is how I did it:
<button type="submit" class="btn" [ngClass]="(formGroup.valid)?'btn-info':''"
[disabled]="!formGroup.valid">Sign in</button>
When the form is valid, button has btn and btn-class (from bootstrap), otherwise just btn class.

We can make class dynamic by using following syntax. In Angular 2 plus, you can do this in various ways:
[ngClass]="{'active': arrayData.length && arrayData[0]?.booleanProperty}"
[ngClass]="{'active': step}"
[ngClass]="step== 'step1'?'active':''"
[ngClass]="step? 'active' : ''"

Let, YourCondition is your condition or a boolean property, then do like this
[class.yourClass]="YourCondition"

The directive operates in three different ways, depending on which of three types the expression evaluates to:
If the expression evaluates to a string, the string should be one or more space-delimited class names.
If the expression evaluates to an object, then for each key-value pair of the object with a truthy value the corresponding key is used as a class name.
If the expression evaluates to an array, each element of the array should either be a string as in type 1 or an object as in type 2. This means that you can mix strings and objects together in an array to give you more control over what CSS classes appear. See the code below for an example of this.
[class.class_one] = "step === 'step1'"
[ngClass]="{'class_one': step === 'step1'}"
For multiple options:
[ngClass]="{'class_one': step === 'step1', 'class_two' : step === 'step2' }"
[ngClass]="{1 : 'class_one', 2 : 'class_two', 3 : 'class_three'}[step]"
[ngClass]="step == 'step1' ? 'class_one' : 'class_two'"

ngClass syntax:
[ngClass]="{'classname' : conditionFlag}"
You can use like this:
<ol class="breadcrumb">
<li [ngClass]="{'active': step==='step1'}" (click)="step='step1'">Step1</li>
<li [ngClass]="{'active': step==='step2'}" (click)="step='step2'">Step2</li>
<li [ngClass]="{'active': step==='step3'}" (click)="step='step3'">Step3</li>
</ol>

This is what worked for me:
[ngClass]="{'active': dashboardComponent.selected_menu == 'profile'}"

For elseif statement (less comparison) use like that: (For example you compare three statement)
<div [ngClass]="step === 'step1' ? 'class1' : (step === 'step2' ? 'class2' : 'class3')"> {{step}} </div>

Not relevant with [ngClass] directive but I was also getting the same error as
Cannot read property 'remove' of undefined at...
and I thought to be the error in my [ngClass] condition but it turned out the property I was trying to access in the condition of [ngClass] was not initialized.
Like I had this in my typescript file
element: {type: string};
and In my [ngClass] I was using
[ngClass]="{'active', element.type === 'active'}"
and I was getting the error
Cannot read property 'type' of undefined at...
and the solution was to fix my property to
element: {type: string} = {type: 'active'};
Hope it helps somebody who is trying to match a condition of a property in [ngClass]

<div class="collapse in " [ngClass]="(active_tab=='assignservice' || active_tab=='manage')?'show':''" id="collapseExampleOrganization" aria-expanded="true" style="">
<ul> <li class="nav-item" [ngClass]="{'active': active_tab=='manage'}">
<a routerLink="/main/organization/manage" (click)="activemenu('manage')"> <i class="la la-building-o"></i>
<p>Manage</p></a></li>
<li class="nav-item" [ngClass]="{'active': active_tab=='assignservice'}"><a routerLink="/main/organization/assignservice" (click)="activemenu('assignservice')"><i class="la la-user"></i><p>Add organization</p></a></li>
</ul></div>
Code is good example of ngClass if else condition.
[ngClass]="(active_tab=='assignservice' || active_tab=='manage')?'show':''"
[ngClass]="{'active': active_tab=='assignservice'}"

Try Like this..
Define your class with ''
<ol class="breadcrumb">
<li *ngClass="{'active': step==='step1'}" (click)="step='step1; '">Step1</li>
<li *ngClass="{'active': step==='step2'}" (click)="step='step2'">Step2</li>
<li *ngClass="{'active': step==='step3'}" (click)="step='step3'">Step3</li>
</ol>

The example is a bit big, but triggering a class instead of typing inline is my first preferred approach.
this way you can add as many possibilities as you want to your element.
There may be a way for those who want to bind more than one [ngClass] to a single element.
<span class="inline-flex items-center font-medium" [ngClass]="addClass">{{ badge.text }}</span>
import { ChangeDetectionStrategy, Component, Input } from '#angular/core';
type Badge = {
size?: 'basic' | 'large';
shape?: 'basic' | 'rounded';
color?: 'gray' | 'red' | 'yellow' | 'green' | 'blue' | 'indigo' | 'purple' | 'pink';
dot?: boolean;
removeButton?: false;
text?: string;
}
#Component({
selector: 'bio-badge',
templateUrl: './badge.component.html',
styleUrls: ['./badge.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BioBadgeComponent {
#Input() badge!: Badge;
get addClass() {
return {
'px-2.5 py-0.5 text-sx': this.badge.size === 'basic',
'px-3 py-0.5 text-sm': this.badge.size === 'large',
'rounded-full': this.badge.shape === 'basic',
'rounded': this.badge.shape === 'rounded',
'bg-gray-100 text-gray-800': this.badge.color === 'gray',
'bg-red-100 text-red-800': this.badge.color === 'red',
'bg-yellow-100 text-yellow-800': this.badge.color === 'yellow',
'bg-green-100 text-green-800': this.badge.color === 'green',
'bg-blue-100 text-blue-800': this.badge.color === 'blue',
'bg-indigo-100 text-indigo-800': this.badge.color === 'indigo',
'bg-purple-100 text-purple-800': this.badge.color === 'purple',
'bg-pink-100 text-pink-800': this.badge.color === 'pink',
}
}
}

If user want to display the class on basis of && and ||
then below one is work for me
[ngClass]="{'clasname_1': condition_1 && condition_2, 'classname_2': condition_1 && condition2, 'classname_3': condition}"
Example:
[ngClass]="{'approval-panel-mat-drawer-side-left': similar_toil_mode==='side' && showsTheSimilarToilsWithCloseIcon, 'approval-panel-mat-drawer-side-right': similar_toil_mode==='side' && !showsTheSimilarToilsWithCloseIcon, 'approval-panel-mat-drawer-over': similar_toil_mode==='over'}"

Related

Using ngClass 'Missing expected }' error being received

I am trying to conditionally add a class to a list item (through a child components selector), depending on the images width size. The width size is hard coded when the new image item is created in another component.
I keep receiving the error message :
'Missing expected }' in my gallery-list.component.html file.
I can't see that I am missing } anywhere. Can anyone help?
Here is the gallery-list html code:
<ul class="container">
<li class="item-list-container" data-masonry='{ "itemSelector": ".grid-item", "columnWidth": 160 }'><app-image-item
class="image-item"
*ngFor="let imageEl of images;"
[image]="imageEl"
[ngClass] ="{
'widthOne': imageEl.widthSize === 50px,
'widthTwo': imageEl.widthSize === 100px,
'widthThree': imageEl.widthSize === 150px,
'widthFour': imageEl.widthSize === 200px,
}"
(click)="onImageSelect(imageEl.id)"
></app-image-item>
</li>
</ul>
That's not a correct syntax. Take a look at this similar demo. Try
<ul class="container">
<li class="item-list-container" data-masonry='{ "itemSelector": ".grid-item", "columnWidth": 160 }'><app-image-item
class="image-item"
*ngFor="let imageEl of images;"
[image]="imageEl"
[ngClass] ="getClass(imageEl.widthSize)"
(click)="onImageSelect(imageEl.id)"
></app-image-item>
</li>
</ul>
in ts file:
getClass(widthSize): string{
if(widthSize === "50px") {
return "widthOne"
}
if(widthSize === "100px") {
return "widthTwo"
}
if(widthSize === "150px") {
return "widthThree"
}
if(widthSize === "200px") {
return "widthFour"
}
}
Note:
I would recommend you to add a property on images array deciding the class which has to be added, rather than using function call as I did using getClass(). Using function on HTML is a bad practice.
If the image is coming from some service, you can use .map and add className property on it.

Angular: Can content of an HTML attribute accessed from within the same HTML tag?

This question is an Angular specific.
I am writing an Angular program and in my template (html) I need to read the content of id' attribute, from within the same tag.
Below is a very simplified example that checks if id has a value named 'good', and then assigns class a value named 'active'.
<li id="good" [className]=" [id] == 'good' ? 'active' : '' "><li>
This is not working. Could you please tell me how you could do this in Angular?
I have three ways to achieve the same goal.
Use ts variable
<li [id]="id"
[className]="id == 'good' ? 'active' : '' ">
Use ts variable
</li>
Use template reference variable
<li id="good"
#li
[className]=" li.id == 'good' ? 'active' : '' ">
Use template reference variable
</li>
Use css selector
<li id="good" class="good-css" >
Use css selector
</li>
.good-css[id=good] {
color: blue
}
https://stackblitz.com/edit/angular-anowbm?file=src/app/app.component.html
Hope to help you.
You can try like this:
Typescript:
id = 'good'
Template:
<li [id]="id"
[class]="id == 'good' ? 'active' : '' ">
<li>

change background color based on response attributes Angular

I had request data from API, then I got response Object, so using *ngFor, I had managed to display some data that I want, problem is that are some css need to be implemented based on response attribute, for my data, I had list of bank and status. based on attribute status Offline
I need to change background color and need only show Offline status only.I had managed to change the background color, this is what I had tried:
html file
<ul class="ul1">
<li
[style.background]="getBackgroundColor(p.status)"
class="li1" *ngFor="let p of myData?.paymentChannels">
<span>{{p.name}}</span>
<br>
<span>{{p.status}}</span>
</li>
</ul>
ts file
getBackgroundColor(status) {
switch (status) {
case 'Offline':
return 'grey';
}
}
expected output:
also this is my stackblitz demo, I could use some suggestion to solve mine.
I would suggest something like this:
<ul class="ul1">
<li
[ngClass]="{offline: p.status == 'Offline'}"
class="li1" *ngFor="let p of myData?.paymentChannels">
<span>{{p.name}}</span>
<br>
<span> {{p.status == 'Offline' ? p.status : ' '}}</span>
</li>
</ul>
And add to css:
.offline {
background-color: gray;
}
your code is working but I think it's better without function like this:
<ul class="ul1">
<li [style.background]="'grey' : p.status == 'Offline'" class="li1"
*ngFor="let p of myData?.paymentChannels">
<span>{{p.name}}</span>
<br>
<span>{{p.status}}</span>
</li>
</ul>
Before
<span>{{p.status}}</span>
After
<span>{{p.status === 'Offline' ? 'Offline' : ' '}}</span>
It would be even better if you would extract the logic to a method withing the TS file.
The ' ' part is a placeholder (blank space) so the box do not collapse, but I would recommend adding the proper CSS styling, for instance, adding min-height: 36px; to the class .li1 would suffice.
StackBlitz
You can filter your data using structural directive *ngIf and then apply styling:
<ul
*ngIf="myData?.paymentChannels"
class="ul1">
<li
[style.background]="p?.status === 'Offline'? 'grey' : 'green'"
class="li1"
*ngFor="let p of myData?.paymentChannels">
<ng-container *ngIf="p?.status === 'Offline'">
<span>{{p.name}}</span>
<br>
<span>{{p.status}}</span>
</ng-container>
</li>
</ul>
If you want to filter and your array of data is small, then you can use *ngIf directive. However, it would be better to use filter pipe:
<li *ngFor="let item of myData?.paymentChannels | yourFilter:filterargs">
and your pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'myfilter',
pure: false
})
export class YourFilterPipe implements PipeTransform {
transform(items: any[], filter: Object): any {
if (!items || !filter) {
return items;
}
// filter items array, items which match and return true will be
// kept, false will be filtered out
return items.filter(item => item.title.indexOf(filter.title) !== -1);
}
}
and you should include your pipe into app.module.ts:
import { YourFilterPipe } from './shared/pipes/your-filter.pipe';
#NgModule({
imports: [
..
],
declarations: [
YourFilterPipe,
],
providers: [
..
],
bootstrap: [AppComponent]
})
export class AppModule { }
Please, see the example at stackblitz.com
UPDATE:
If you do not want to filter data, but hide status offline you can use *ngIf structural directive:
<ul
*ngIf="myData?.paymentChannels"
class="ul1">
<li
[style.background]="p?.status === 'Offline'? 'grey' : 'green'"
class="li1"
*ngFor="let p of myData?.paymentChannels">
<span>{{p.name}}</span>
<br>
<span *ngIf="p?.status === 'Offline'; else empty">{{p.status}}</span>
<ng-template #empty>$nbsp;</ng-template>
</li>
</ul>

How to give multiple conditions in *ngIf statement in angular 6?

My navigation bar,
<div *ngIf = ("path == '/login'" && "path == '/home'") class="tabs-header-nav">
<a routerLink="/login" class="nav-link active">login</a>
<a routerLink="/bb" class="nav-link">home</a>
<a routerLink="/" class="nav-link">CC</a>
</div>
<div *ngIf = ("path == '/aa'" || "path == '/bb'") class="tabs-header-nav">
<a routerLink="/aa" class="nav-link active">aa</a>
<a routerLink="/bb" class="nav-link">bb</a>
<a routerLink="/" class="nav-link">CC</a>
</div>
Likewise I have 5 to 6 navigation bar in my html. Now I need to display a particular navigation bar for particular page. Single if condition ( *ngIf = "path === '/aa'" ) seems to be working, where if I give multiple conditions it is not working. Could you please help me on this?
You should go for ngSwitch that takes multiple values
<div [ngSwitch]="path">
<div *ngSwitchCase="'/login'">...</div>
<div *ngSwitchCase="'/home'">...</div>
</div>
I have created a Stackblitz demo here
I think you are doing mistake in *ngIf quotations
If you have data that may in every time be different, mostly you use of If. but if is not ok in every where. It is better that you use of switch-case in your code like:
public string TestSwitchCase(string value)
{
switch (value)
{
case "John":
return null;
case "Jack":
return "Jack";
default:
break;
}
return null;
}
Now in angular, there is best way to do this in HTML code. this is ngSwitch and use like:
<container-element [ngSwitch]="switch_expression">
<some-element *ngSwitchCase="match_expression_1">...</some-element>
<some-element *ngSwitchCase="match_expression_2">...</some-element>
<some-other-element *ngSwitchCase="match_expression_3">...</some-other-element>
<ng-container *ngSwitchCase="match_expression_3">
<!-- use a ng-container to group multiple root nodes -->
<inner-element></inner-element>
<inner-other-element></inner-other-element>
</ng-container>
<some-element *ngSwitchDefault>...</some-element>
</container-element>
For more information See: This
GoodLuck.
[SOLUTION] : Simply use "&&" operator to add multiple conditions in *ngIf -
//TypeScript File - demo.ts
//Declaration
flag: any;
temp_array = [];
//Assignment
this.flag = true;
this.temp_array = [1,2];
<!--HTML File - demo.html-->
<div *ngIf="flag && temp_array.length>0">
Both *ngIf conditions passes
</div>

Bind class based on knockout foreach index

I need to make the first item in a tab group, active. I am attempting to use the index for the foreach, and set the class based on that.
<ul class="nav nav-tabs" data-bind="foreach: TemplateGroups">
<li data-bind="css: {active: $index==0}"><a data-toggle="tab" href="#tb1"><span data-bind="text: Description"></span></a></li>
</ul>
However, the item isn't becoming active. What am I missing?
From Binding context
$index is an observable and is updated whenever the index of the item changes
And an observable is a function.
Then, you are comparing a function with a number, and always is false.
You should use active: $index() == 0 or $index() === 0.
Example: Codepen