ng-class with multiple options - angularjs

I'm fetching data using a REST API and one of the attributes returned can be any of 3 options;
The options are; Negative, Warning, Success.
I want to set a class using ng-class based on the value returned;
I'm able to do this but only for one;
Code below;
<div class="alert" ng-class="restaurantNotice(restaurant[0].notice)" ng-if="restaurant[0].notice.length">
<div class="alert-inner inner-large" ng-bind="restaurant[0].notice"></div>
</div>
$scope.restaurantNotice = function(a) {
return (a = Negative) ? 'negative' : '';
};
As you can see, if a = Negative then a class negative is applied else nothing is applied.
Now I want to check for all three options and apply 3 different classes respectively

Instead of function in ng-class, use object
ng-class="{'negative' : restaurant[0].notice == 'Negative', 'warning' : restaurant[0].notice == 'Warning', 'success' : restaurant[0].notice == 'Success'}"

You can try
<div class="alert" ng-class="{'negative' : restaurant[0].notice.length, 'warning' : restaurant[1].notice.length, 'success' : restaurant[2].notice.length}">
<div class="alert-inner inner-large" ng-bind="restaurant[0].notice"></div>
</div>
duplicate of Adding multiple class using ng-class

In this way:
<p ng-class="{a: deleted, b: important, c: error}">Example</p>
if a is true, apply class deleted, and so on.

If you are just going to set the same notice type (Negative, Warning, Success) as class then just convert to lower case and return from the function instead of putting conditions.
$scope.restaurantNotice = function(a) {
return a.toLowerCase();
};
OR
<div class="alert" class="{{restaurant[0].notice | lowercase}}" ng-if="restaurant[0].notice.length">

Related

ng-class not working with ng-repeat

I have a situation where I have to add class according to the condition and the ng-class is working according to it even the condition in the ng-class is true.
<ul id="" class="clowd_wall" dnd-list="vm.cardData[columns.id].data"
dnd-drop="vm.callback(item,{targetList: vm.cardData[columns.id].data, targetIndex: index, event: event,item:item,type:'folder',eventType:'sort','root':'folder',current_parent:'folder'})" ng-model="vm.cardData[columns.id].data">
<div class="emptyCol" ng-if="vm.cardData[columns.id].data.length==0">Empty</div>
<li class="dndPlaceholder"></li>
<li class="cont____item" ng-repeat="card in vm.cardData[columns.id].data | orderBy:vm.sort" dnd-draggable="card"
dnd-effect-allowed="move"
dnd-allowed-types="card.allowType"
dnd-moved="vm.cardData[columns.id].data.splice($index, 1)"
dnd-selected="vm.tree.selected = card" ng-class="{emptyCard:card.data.length==0,zoomin:vm.zoomin=='zoomin',emptyCard:!card.data}">
<div class="item" style="height:79%">
<ng-include ng-init = "root = columns.id" src="'app/partials/card.html'"></ng-include>
</div>
</li>
</ul>
ng-class="{'emptyCard': (!card.data || !vm.cardData[columns.id].data.length),'zoomin':(vm.zoomin=='zoomin')}">
Seems like you want to use vm.cardData[columns.id].data.length instead of card.data.length
Your question is not clear as don't know what card.data will contain and ".data" will be present for each iteration
If it is array then this will work "card.data.length" and if there is no "data" key in "card" then ".length" will through error i.e. if card.data itself undefined then it will not have "length" property.
Try to add condition in ng-class one by one then you will be able to figure out which condition is causing problem.
Made some small change
ng-class="{emptyCard: card.data.length==0 || !card.data,zoomin: vm.zoomin=='zoomin'}"
If you have multiple expression, try old fashioned, if it looks best:
Controller:
$scope.getcardClass = function (objCard, strZoomin) {
if (!card.data) {
return 'emptyCard';
} else if (strZoomin =='zoomin') {
return 'zoomin';
} else if (card.data.length == 0) {
return 'emptyCard';
}
};
HTML:
ng-class="vm.getcardClass(card, vm.zoomin)"
NOTE: Replace vm with your controller object.

ngClass Directive: Can I use multiple expressions to add multiple classes?

Here is an example of what I want to achieve
data-ng-class="{ 'tooltip_show' : showTooltip , 'tooltip__' + brand.settings.name }"
but it doesn't work.
Use the array form for ng-class:
<div ng-class="[showTooltip ? 'tooltip_show' : '',
'tooltip__' + brand.settings.name]">
<div>
OR compute the class in JavaScript:
<div ng-class="computeClass(tooltip_show, brand.setting.name)">
</div>
$scope.computeClass(show, name) {
var obj = {};
obj.showTooltip = show;
obj['tooltip_'+name] = true;
return obj;
};
The later approach is more easily debugged and better for complex computation.
See also,
AngularJS ng-class Directive Reference - Known Issues
AngularJS Developer Guide - Why mixing interpolation and expressions is bad practice
It looks like you haven't set a value for the second item. Did you mean something like
{ 'tooltip_show' : showTooltip , 'tooltip__' + brand.settings.name : tooltipText }
or
{ 'tooltip_show' : showTooltip , 'tooltip__' : brand.settings.name }
?
http://jsbin.com/genaqapefe/edit?html,js,output
data-ng-class="{ 'tooltip_show': showToolTip, {{ 'tooltip_' + brand.settings.name }}: true }"
This is working for me in this bin. I couldn't get it to evaluate without the curly braces, although not sure if that's the best practice.

AngularJS : custom filter with special rules

I'm trying to make multi filter, that filters three properties in object.
what I already did:
<div class="" ng-repeat="selectedCard in Cards | filter {
status:filterValueStatus,
monitorLevel:filterValueType,
monitorSystem:filterValue
} ">
The problem is that I need to show the object if status equals to '2' , and not continue to the two filtered properties : monitorLevel and monitorSystem that come after. (It doesn't work).
Also I need to show the object if status equals to '3' , then filter it by the two properties monitorLevel and monitorSystem.
In summary it needs to check the status and only after , to decide whether to make the other two filters or not.
The filter I built until now :
app.filter('cardFilter', ['$filter', function($filter) {
return function(status, monitorLevel,monitorSystem) {
if (status == '2') {
return $filter('filter')(status);
} else {
return $filter('filter')(status,monitorLevel,monitorSystem);
}
};
}]);
the answer :
<div class="" ng-repeat="selectedCard in Cards | filter: filterValueStatus == '2' ? {status:'2'} : {status:'3', monitorLevel:filterValueLevel, monitorSystem:filterValue}">
You can split your filters into 2 and use ternary operation. You get the concept:
<div ng-repeat="selectedCard in Cards | filter : { status:filterValueStatus} |
filter : selectedCard.status === 2 ? {} : {
monitorLevel:filterValueType,
monitorSystem:filterValue
}
} ">
the answer is not so complex as I thought it would be as start.
<div class="" ng-repeat="selectedCard in Cards | filter: filterValueStatus == '2' ? {status:'2'} : {status:'3', monitorLevel:filterValueLevel, monitorSystem:filterValue}">
It first , check if status equals to '2' and only afterwords filters the cards, according to the status.

ng-class one time binding

I'm wondering if it's possible to have a ng-class with class one time binded and class which are evaluated each digest cycle.
<div ng-class="{'one_time_binded_class': isMonkey(), 'not_one_time_binded_class': isUnicorn()}"></div>
I know I can one time bind the complete ng-class with ng-class="::{...}"
but my need is to one time bind a particular expression
Of course, this thing doesn't work :
<div ng-class="{'my_static_class': ::isMonkey(), 'my_dynamic_class': isUnicorn()}"></div>
Is there a way to do it ?
Method 1:
class="some-class {{::expression ? 'my-class' : ''}}"
Method 2:
ng-class="::{'my-class': expression}"
One way I can think of doing this (if I followed what you were trying to say) is as follows...
.blue{
color: blue;
}
.underline{
text-decoration: underline;
}
.lineThrough{
text-decoration: line-through;
}
<div ng-app ng-controller="myCtrl">
<p ng-class="{'blue': isMonkey()}" class="{{isUnicorn() ? dynamicClass: ''}}">My Text</p>
<button ng-click="monkey = !monkey">Monkey</button>
<button ng-click="unicorn = !unicorn">Unicorn</button>
<button ng-click="toggleClass()">Toggle</button>
</div>
function myCtrl($scope) {
$scope.dynamicClass = "underline";
$scope.monkey = true;
$scope.unicorn = true;
$scope.isMonkey = function () {
return $scope.monkey;
}
$scope.isUnicorn = function () {
return $scope.unicorn;
}
$scope.toggleClass = function(){
$scope.dynamicClass = $scope.dynamicClass === "underline"? "lineThrough": "underline";
}
}
JSFiddle
An important part of one time binding is that it not be bound until the 'expression' is not undefined. The best answer so far, by #ifadey, his Method 1 evaluates to an empty string when 'expression' is undefined, which get's bound. This is contrary to the expected feature behavior. Method 2 is equally unhelpful in this late binding scenario.
Do do this correctly, directly answering op's question:
class="some-class {{::expression ? 'one-time-class' : undefined}}"
ng-class="{ 'my-dynamic-class' : expression2 }"
or the more technically correct but ugly:
class="some-class {{::expression ? 'one-time-class' : (expression===undefined ? undefined : '')}}"

ngClass string binding expression with class map

Is it possible to use ngClass with an expression AND a class map? I want to conditionally add a class based on the existence of a variable as well as use that variable in the expression that creates the class.
For instance, if isActive() is true and getStatus() returns "valid" I want the class list to be "element element--active element--valid". If getStatus() returns undefined I want the class list to be "element element--active".
<div
class="element"
ng-class="{
'element--active': ctrl.isActive(),
'element--{{ ctrl.getStatus() }}': ctrl.getStatus()
}"></div>
Doesn't seem to work.
<div
class="element element--{{ctrl.getStatus()}}"
ng-class="{
'element--active': ctrl.isActive()
}"></div>
Works but then there's an extra hanging "element--" if getStatus() returns undefined.
Do I have to add a method in my controller to handle the class generation?
i'd suggest to make just one function call to get the classes. It will make it cleaner and have the class logic in one place.
In your controller:
this.getElementStatus = function(){
var rules = {active:this.isActive()}; //Prefix with element-- {element--active:}
rules[this.getStatus()] = true; //Prefix with element--, rules['element--' + this.getStatus()] = true
return rules;
}
and your view would just be:
<div
class="element"
ng-class="ctrl.getElementStatus()"></div>
It seems like your element-- is redundant with the rule instead make use of cascadeability(CSS) property. and define rules as :
Example:
.element.active{ /*....*/ }
.element.success {/*...*/}
.element.error{/*...*/}
This will help in maintenance, gets more verbose and get to the natural way of adding css rules and could remove these kind of complexities from the view.
You could as well do:
<div class="element"
ng-class="{'active': ctrl.isActive(), '{{ctrl.getStatus()}}':true}"
or :
<div class="element"
ng-class="[ctrl.isActive() ? 'active' : '', ctrl.getStatus()]"
If you don't mind getting a true added as a rule(should not affect anything anyways) then,
<div class="element"
ng-class="[!ctrl.isActive() || 'element--active' , 'element--' + ctrl.getStatus()]">
You can use class and ng-class map on the same element. But since your class name is dynamic you will have to something like this.
<div
ng-class="'element '
+ (ctrl.isActive() ? ' element--active' : '')
+ (ctrl.getStatus() ? ' element--' + ctrl.getStatus() : '')"></div>

Resources