AngularJS ng-class if-else expression - angularjs

With AngularJS I'm using ng-class the following way:
<div class="bigIcon" data-ng-click="PickUp()"
ng-class="{first:'classA', second:'classB', third:'classC', fourth:'classC'}[call.State]"/>
I'm wondering if I can use the if-else expression to do something similar to this:
<div class="bigIcon" data-ng-click="PickUp()"
ng-class="{first:'classA', second:'classB', else:'classC'}[call.State]"/>
So whenever call.State differs from first or second use classC and avoid specifying each value?

Use nested inline if-then statements (Ternary Operators)
<div ng-class=" ... ? 'class-1' : ( ... ? 'class-2' : 'class-3')">
for example :
<div ng-class="apt.name.length >= 15 ? 'col-md-12' : (apt.name.length >= 10 ? 'col-md-6' : 'col-md-4')">
...
</div>
And make sure it's readable by your colleagues :)

you could try by using a function like that :
<div ng-class='whatClassIsIt(call.State)'>
Then put your logic in the function itself :
$scope.whatClassIsIt= function(someValue){
if(someValue=="first")
return "ClassA"
else if(someValue=="second")
return "ClassB";
else
return "ClassC";
}
I made a fiddle with an example : http://jsfiddle.net/DotDotDot/nMk6M/

I had a situation where I needed two 'if' statements that could both go true and an 'else' or default if neither were true, not sure if this is an improvement on Jossef's answer but it seemed cleaner to me:
ng-class="{'class-one' : value.one , 'class-two' : value.two}" class="else-class"
Where value.one and value.two are true, they take precedent over the .else-class

Clearly! We can make a function to return a CSS class name with following fully example.
CSS
<style>
.Red {
color: Red;
}
.Yellow {
color: Yellow;
}
.Blue {
color: Blue;
}
.Green {
color: Green;
}
.Gray {
color: Gray;
}
.b{
font-weight: bold;
}
</style>
JS
<script>
angular.module('myapp', [])
.controller('ExampleController', ['$scope', function ($scope) {
$scope.MyColors = ['It is Red', 'It is Yellow', 'It is Blue', 'It is Green', 'It is Gray'];
$scope.getClass = function (strValue) {
if (strValue == ("It is Red"))
return "Red";
else if (strValue == ("It is Yellow"))
return "Yellow";
else if (strValue == ("It is Blue"))
return "Blue";
else if (strValue == ("It is Green"))
return "Green";
else if (strValue == ("It is Gray"))
return "Gray";
}
}]);
</script>
And then
<body ng-app="myapp" ng-controller="ExampleController">
<h2>AngularJS ng-class if example</h2>
<ul >
<li ng-repeat="icolor in MyColors" >
<p ng-class="[getClass(icolor), 'b']">{{icolor}}</p>
</li>
</ul>
<hr/>
<p>Other way using : ng-class="{'class1' : expression1, 'class2' : expression2,'class3':expression2,...}"</p>
<ul>
<li ng-repeat="icolor in MyColors">
<p ng-class="{'Red':icolor=='It is Red','Yellow':icolor=='It is Yellow','Blue':icolor=='It is Blue','Green':icolor=='It is Green','Gray':icolor=='It is Gray'}" class="b">{{icolor}}</p>
</li>
</ul>
You can refer to full code page at ng-class if example

The above solutions didn't work for me for classes with background images somehow. What I did was I create a default class (the one you need in else) and set class='defaultClass' and then the ng-class="{class1:abc,class2:xyz}"
<span class="booking_warning" ng-class="{ process_success: booking.bookingStatus == 'BOOKING_COMPLETED' || booking.bookingStatus == 'BOOKING_PROCESSED', booking_info: booking.bookingStatus == 'INSTANT_BOOKING_REQUEST_RECEIVED' || booking.bookingStatus == 'BOOKING_PENDING'}"> <strong>{{booking.bookingStatus}}</strong> </span>
P.S: The classes that are in condition should override the default class i.e marked as !important

This is the best and reliable way to do this.
Here is a simple example and after that you can develop your custom logic:
//In .ts
public showUploadButton:boolean = false;
if(some logic)
{
//your logic
showUploadButton = true;
}
//In template
<button [class]="showUploadButton ? 'btn btn-default': 'btn btn-info'">Upload</button>

A workaround of mine is to manipulate a model variable just for the ng-class toggling:
For example, I want to toggle class according to the state of my list:
1) Whenever my list is empty, I update my model:
$scope.extract = function(removeItemId) {
$scope.list= jQuery.grep($scope.list, function(item){return item.id != removeItemId});
if (!$scope.list.length) {
$scope.liststate = "empty";
}
}
2) Whenever my list is not empty, I set another state
$scope.extract = function(item) {
$scope.list.push(item);
$scope.liststate = "notempty";
}
3) When my list is not ever touched, I want to give another class (this is where the page is initiated):
$scope.liststate = "init";
3) I use this additional model on my ng-class:
ng-class="{'bg-empty': liststate == 'empty', 'bg-notempty': liststate == 'notempty', 'bg-init': liststate = 'init'}"

Use it this way:
<div [ngClass]="{cssClass A: condition 1, cssClass B: condition 2, cssClass C: condition 3}">...</div>

You can try this method:
</p><br /><br />
<p>ng-class="{test: obj.value1 == 'someothervalue' || obj.value2 == 'somethingelse'}<br /><br /><br />
ng-class="{test: obj.value1 == 'someothervalue' || obj.value2 == 'somethingelse'}
You can get complete details from here.

Related

Angular 6 *ngFor display different styles for first, odd, even and last

I am learning Angular 6 and just trying to put togheter some of the stuff I have learned and I am currently running into an issue that I cannot find an answer to. I am trying to change the style of a LI using *ngFor depending if the index is First, Last, Odd or Even. So far everything works but I can't figure out how to do it for the Last because everything I add a new object to my list, it is obviously the last so it render the color for the last.
I understand how to do it but the real problem is that I am adding stuff dynamicly to my list from a form and I'm not sure how to evaluate the Last so that the others become to right color.
Keep in mind that I am still a newb and it might look messy and I also understand that some client-side validations I am doing are probably not optimal or required since HTMl5 but I made it to learn.
Here is my code for my component HTML
>
<h1>List of courses :</h1><br>
<div *ngIf="courses.length > 0; then coursesList else noCourses"></div>
<ng-template #coursesList>
<h2>List of Courses :</h2>
<ul *ngFor="let course of courses; index as i;">
<li [ngStyle]="{'background-color':getColor(i)}" style="color: white;">
<strong>Index : </strong>{{i}} <strong>ID : </strong>{{course.id}} <strong>Name</strong> : {{course.name}}
<button (click)="onRemove(i)">Remove</button>
<button (click)="onModify(i)">Modify</button>
</li>
</ul>
</ng-template>
<ng-template #noCourses>
<h5>There are no courses in this list. Use the form bellow to add some.</h5>
</ng-template>
<div (keyup.enter)="onAdd()">
<span>ID : <input type="number" (keypress)="checkNumber($event)" [(ngModel)]="fields.id" placeholder="Enter an ID"></span>
<span>Name : <input type="text" [(ngModel)]="fields.name" placeholder="Enter a NAME"></span>
<button (click)="onAdd()">Add</button>
<button (click)="onClear()">Clear</button>
</div>
<div *ngIf="isNotNumber" style="background-color: red; color:black"><strong>ID can only be numbers !</strong></div>
<div *ngIf="noValues" style="background-color: red; color:black"><strong>Please fill all fields !</strong></div>
<div *ngIf="noModifyValues" style="background-color: red; color:black"><strong>To modify enter all informations!</strong></div>
Code for .TS
>
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
noValues: boolean;
noModifyValues: boolean;
isNotNumber: boolean;
fields: Courses = {id: null, name: null};
courses: Array<Courses> = [];
viewMode: string = null;
checkNumber($event) {
if ($event.keyCode != 13) {
isFinite($event.key) ? this.isNotNumber = false : this.isNotNumber = true;
}
}
onAdd() {
if (!this.fields.id || !this.fields.name) {
this.noValues = true;
} else {
this.courses.push({id: this.fields.id, name: this.fields.name});
this.fields.id = null;
this.fields.name = null;
this.noValues = false;
}
}
onRemove(i) {
this.courses.splice(i, 1);
}
onClear() {
this.courses = [];
this.fields.id = null;
this.fields.name = null;
this.noValues = false;
}
onModify(i) {
if (!this.fields.id || !this.fields.name) {
this.noModifyValues = true;
} else {
this.courses[i].name = this.fields.name;
this.courses[i].id = this.fields.id;
this.noModifyValues = false;
}
}
getColor(i){
if (i % 2 === 0 && i != 0){i = 'odd';}
switch (i) {
case i = 0 : return 'orange';
case i = 'odd' : return 'blue';
}
return 'red';
}
}
interface Courses {
id: number;
name: string;
}
Image of the code in action for better understanding.
If you only want change the background-color you can use [style.background-color] and you can use ternary operator in the .html
<ul *ngFor="let course of courses; let index=i;
let odd=odd;
let last=last;
let first=first">
<li [style.backgound-color]="first?'orange':last?'purple':odd?'blue':'red'">
...
</li>
</ul>
Try something like this
getColor(i){
if (i % 2 === 0 && i != 0){i = 'odd';}
if (this.courses && (this.courses.length - 1 === i)) {i = 'last'}
switch (i) {
case i = 0 : return 'orange';
case i = 'odd' : return 'blue';
}
return 'red';
}
Hope it works - Happy coding !!
Thanks Rahul. The part I was missing is evaluating if there is something in courses. However, I had to had a few more lines to Odd and Last as follow :
getColor(i){
if (this.courses && i != 0 && (this.courses.length - 1 === i)) {i = 'last'}
if (i % 2 === 0 && i != 0 && i != 'last'){i = 'odd';}
switch (i) {
case i = 0 : return 'orange';
case i = 'odd' : return 'blue';
case i = 'last' : return 'purple';
}
return 'red';
}
Quick question. It seems like a whole lot of IF and && and checking specific things. Is that the way to do it properly?
You could use if else ladder instead of mixing up if else and switch and assignments like given below
getColor(i)
{
if(this.courses)
{
if(i==0)
return "orange";
else if(i==this.courses.length-1)
return "purple";
else if (i%2==0)
return "red";
else
return "blue";
}
}

AngularJS - Using custom filter in ng-repeat for prefixing comma

Need to remove comma if value is empty works good if I have value
present at start or middle; But same doesn't work in this scenario.
app.filter('isCSV', function() {
return function(data) {
return (data !== '') ? data + ', ' : '';
};
});
Angularjs ng repeat for addressline - Plunker
I would instead operate on arrays of properties and use a pair of filters, one to remove empty values, and one to join the array.
This way it's very explicit about what properties you are displaying.
<body ng-controller="MainCtrl">
<ul>
<li ng-repeat="item in details">
{{ [ item.address0, item.address1, item.address2, item.address3] | removeEmpties | joinBy:', ' }}
</li>
</ul>
</body>
With the following filters:
app.filter('removeEmpties', function () {
return function (input,delimiter) {
return (input || []).filter(function (i) { return !!i; });
};
});
app.filter('joinBy', function () {
return function (input,delimiter) {
return (input || []).join(delimiter || ',');
};
});
Here's the updated Plunkr
Tricky but should work in your case Also no filter need
{{ item.address0 }} <span ng-if="item.address1">,
</span> {{ item.address1}}<span ng-if="item.address2">,</span>{{
item.address2}}
<span ng-if="item.address3">,</span>{{ item.address3}}
Here is working example
I would prefer writing a function instead of adding a filter so many times.
$scope.mergeAddresses = function(item) {
var address = item.address0;
[1,2,3].forEach(function(i) {
var add = item["address"+i];
if (!add) return;
address += (address ? ", " : "") + add;
});
if (address) address += ".";
return address;
}
Plunker

How to update html element in ng-repeat collection every second automatically

In my controller I have a function that receives data from API every 2 seconds($interval). This data is rendered and displayed to user. When I get positive numbers from my API I want to set background color in HTML to green for 1 second and return it to original color. If it is negative, set background color to red for 1 second, and so on...
controller.js
function checkForUpdatedIndices(){
dataService.getIndices().then(function(res){
$scope.recentIndeces = res.data;
});}
var indicesTimer = setInterval(checkForUpdatedIndices,2000);
checkForUpdatedIndices();
My HTML:
<ul id="ctr_indices">
<li class="cl_indeces" ng-repeat="i in recentIndeces track by $index">
<span class="itemname">{{i.itemName}}</span>
<span class="itemlastvalue">{{i.itemLastValue}}</span>
<span class="itemchange">{{i.itemChange}}</span>
<span class="itempercentage">{{i.itemPercentageChange}}</span>
</li>
</ul>
When i.itemLastValue contains "+" I want to see it green for 1 second and after that change it back to original color.
Thanks in advance
you can do this using ng-style directive. set this style like this
ng-style="{'background-color' : i.color}"
and call a function inside ng-init and pass the item as a parameter
<span class="itemlastvalue" ng-style="{'background-color' : i.color}" ng-init="setColor(i)">{{i.itemLastValue}}</span>
In the function assign the color according to the condition and use timeout function to reverse it to the original value
$scope.setColor = function(i){
if( i.itemLastValue == "+"){
i.color = 'green'
}
else if( i.itemLastValue == "-"){
i.color = 'red'
}
$timeout(function(){
i.color = "white" // what ever the original value
},1000)
}
UPDATED
the second scenario is removing the ng-init function and call the function inside checkForUpdatedIndices
function checkForUpdatedIndices(){
dataService.getIndices().then(function(res){
$scope.recentIndeces = res.data;
setColor()
});
}
function setColor(){
var backUp = angular.copy($scope.recentIndeces);
for(var i=0; i<= $scope.recentIndeces.length-1; i++){
if( $scope.recentIndeces[i].itemLastValue == "+"){
$scope.recentIndeces[i].color = 'green'
}
else if( $scope.recentIndeces[i].itemLastValue == "-"){
$scope.recentIndeces[i].color = 'red'
}
}
$timeout(function(){
$scope.recentIndeces = angular.copy(backUp);
},1000)
}

setting a class based on an angular property

I want to do something like this
ng-class="'notification-{{notification.severity}}' : notification.severity"
and have the result be <div class="notification-warn"></div> when the notification.severity is "warn"
currently I get <div class="notification-"></div>
EDIT: this is an example of what I want to do
https://jsfiddle.net/flashjammin/tgt3tok2/
Try this
<div ng-if="notification.severity" ng-class="'notification-{{notification.severity}}' : notification.severity == 'warn'"></div>
You can try the ternary operator to toggle between class.
Set the value of 'showNotification' in controller.
Set the values of notification.severity as whole
i.e. notification.severity = "notification-warn";
Controller:
if(notification.severity != '' || notification.severity != null){
$scope.showNotification = true;
}
else{
$scope.showNotification = false;
}
HTML:
<div ng-class="showNotification? 'notification' : notification.severity">

Angular: Reinclude null values when filter parameter is empty

I have a pretty simple textbox filtering an ng-repeat on some unordered lis. When I add a value to the textbox the items with the null values are removed and do not return even when the textbox is cleared. I have an idea of why this is happening (the search object now has an empty property which doesn't match the nulls), but I cannot figure out how to solve the problem. I've tried to pop() the property off of the search object with no luck.
HTML:
<div ng-controller="ListCtrl">
<input type="text" ng-model="search.age" placeholder="Age"></input>
<ul>
<li ng-repeat="item in items | filter:search">
{{item.name}} - {{item.age}}
</li>
</ul>
</div>
JS:
function ListCtrl($scope) {
$scope.items = [
{'name':'Carl', 'age':69},
{'name':'Neil', 'age':54},
{'name':'Richard'},
{'name':'Chris', 'age':58}
];
}
Please checkout the JSfiddle to better illustrate the issue.
I figured it out with the help of this answer. If I just add an ng-change to the textbox I can watch for an empty value and delete the property.
HTML:
<input type="text" ng-model="search.age" ng-change="clear()" placeholder="Age"></input>
JS:
$scope.clear = function(){
if($scope.search.age.length == 0){
delete $scope.search.age;
}
}
Updated fiddle. I am aware the current if prevents a user from filtering on a single space, but so far this does not seem to cause a problem for me.
BONUS: ! will return all null values and !! will return all not null values.
The cleanest solution I have found is writing a custom directive to modify the input field behaviour like this:
app.directive('deleteIfEmpty', function () {
return {
restrict: 'A',
scope: {
ngModel: '='
},
link: function (scope, element, attrs) {
scope.$watch("ngModel", function (newValue, oldValue) {
if (typeof scope.ngModel !== 'undefined' && scope.ngModel.length === 0) {
delete scope.ngModel;
}
});
}
};
});
And use it as follows:
<input type="text" ng-model="filter" delete-if-empty>
Modify the input ng-model:
<input type="text" ng-model="searchObj.age" placeholder="Age"></input>
Add this to your controller:
$scope.searchObj = {
}
And either of these will work in your html repeat:
ng-repeat="item in items | filter: searchObj.age"
Or
ng-repeat="item in items | filter: {age: searchObj.age || undefined}"
jsfiddle
You won't be able to use filter:search. Looking at the Angular code, if your obj with an undefined age gets filtered (even when the input is empty) it will fall through this switch statement and always return false. This switch doesn't get called the first time your ng-repeat is run because $scope.search.age is undefined. After your first entry into the input and clearing it out, now $scope.search.age is an empty string...so the filter will always be run.
switch (typeof obj) { ***<-- obj is undefined when you have a missing age***
case "boolean":
case "number":
case "string":
return comparator(obj, text);
case "object":
switch (typeof text) {
case "object":
return comparator(obj, text);
default:
for ( var objKey in obj) {
if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
return true;
}
}
break;
}
return false;
case "array":
for ( var i = 0; i < obj.length; i++) {
if (search(obj[i], text)) {
return true;
}
}
return false;
default:
return false; ***<--falls through and just returns false***
}
You can try writing your own filter function, something like this.
http://jsfiddle.net/wuqu2/
<div ng-controller="ListCtrl">
<input type="text" ng-model="search.age" placeholder="Age"></input>
<ul>
<li ng-repeat="item in items | filter:checkAge">
{{item.name}} - {{item.age}}
</li>
</ul>
</div>
$scope.checkAge = function(item)
{
if($scope.search && $scope.search.age && $scope.search.age.length > 0)
{
return item.age && item.age.toString().indexOf($scope.search.age) > -1;
}
return true;
}

Resources