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

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.

Related

checking multiple condition in ng-if

I need to check 4 conditions in ng-if which are boolean conditions. If any of them is true, I need to display some div.
Is there any simple and better way to do it please?
<div ng-if="ctrl.survivor || ctrl.doctor || ctrl.patient || ctrl.beneficiary">
<div ng-bind-html=" {{ myContent }}">
</div>
</div>
In general, complex logic inside of angular expressions is a code smell:
hard to read
hard to debug
hard to test
In general, I try to avoid boolean logic or anything else that indicates complexity in angular expressions that I write or code review. Instead, I recommend that you extract the logic into a controller method and call the controller method directly. This allows you to also write a nice unit test for it. The final code that I would recommend would look something like this:
<div ng-if="ctrl.isApplicable()">
<div ng-bind-html=" {{ myContent }}"></div>
</div>
In the controller:
class Ctrl {
...
isApplicable() {
return survivor || ctrl.doctor || ctrl.patient || ctrl.beneficiary;
}
...
}

ng-class with multiple options

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">

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 : '')}}"

AngularJS data-binding

I have two code examples that work good, but I don't understand the difference.
So, the first is:
AngularJS:
$scope.state = {presentation: true};
HTML:
<div ng-class="{{state.presentation}} ? 'on' : 'off'"></div>
And the second one:
AngularJS:
$scope.presentation = true;
HTML:
<div ng-class="presentation ? 'on' : 'off'"></div>
Why can't I use something like this in the 2-nd example:
<div ng-class="{{presentation}} ? 'on' : 'off'"></div>
When I use {{ }} with $scope.presentation = true; it doesn't work in ng-class, but I can use {{presentation}} like the text, I mean <div>{{presentation}}</div> and it works good. Why?
But with $scope.state = {presentation: true}; I can use {{presentation}} even in ng-class and it works good.
What the difference?
You should'nt use an expression inside a ng-attribute like that. And it will fail (without or without the state object) :
Error: [$parse:syntax] Syntax Error: Token '?' not a primary
expression at column 2 of the expression [ ? 'on' : 'off'] starting at
[? 'on' : 'off'].
That's normal because angularJs executes it as an angularJS expression, so when you're typing :
<div ng-class="presentation ? 'on' : 'off'"></div>
Internally angularJS executes it as an expression and it can be written also like that :
<div class="{{presentation ? 'on' : 'off'}}"></div>

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