ngClass string binding expression with class map - angularjs

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>

Related

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.

How to do a logic operation in Angular Template

All:
I wonder how can I set a OR operator on a HTML string in Angualar Template, something like:
<div>{{value || <h6>No Header for now.</h6>}}</div>
The logic is if value is a string but not undefined, we show the value text, otherwise we show a error "No Header for Now" wrapped up by <h6>.
I do not know why this expression can not be correctly interpreted?
Thanks
This can be solved with ng-if:
<div ng-if="value">value</div>
<div ng-if="!value"><h6>No Header for now.</h6></div>
You can add specific attributes (e.g. class) and/or directives (e.g. ng-click) on each <div>.
The problem with using a single element is that you have to repeat your condition several times:
<div ng-class="{ value: 'class1', !value: 'class2' }"
ng-click="value ? action1() : action2()"
ng-bind-html="value || html">
</div>
You have to put a string, not an expression: <h6>No Header for now.</h6> is an invalid js expression. '<h6>No Header for now.</h6>' is a string and can be displayed in the {{ }}.
<div>{{value || '<h6>No Header for now.</h6>' }}</div>
or
<div>{{value != null ? value : '<h6>No Header for now.</h6>' }}</div>
I'm convinced the 2nd works.
EDIT:
If you want to add html code in the {{ }}, it is another problem. See AngularJS : Insert HTML into view, the filter 'sanitize' in 2nd answer should help you ( call {{ '<h1>test</h1>' | sanitize }} and it should work.
EDIT 2:
In a js file:
angular.module('yourapp')
.filter("sanitize", ['$sce', function($sce) {
return function(htmlCode){
return $sce.trustAsHtml(htmlCode);
}
}]);
In view:
<div>{{value || '<h6>No Header for now.</h6>' | sanitize }}</div>
In dont know if you need 'ngResource' to use $sce, if it doesn't work, you will have to install angular-resource :/

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

Difficulty adding multiple classes wit ng-class

I'm trying to add two classes to this div using ng-class, but even though checkForActive is returning true, it's being ignored and only class_{{$index}} is getting added.
If I remove class_{{$index}} altogether, active is added correctly.
Is there an obvious mistake in my syntax here?
<div "ng-class="{active: checkForActive, disabled: checkForDisable, class_{{$index}}} "></div>
You could just provide true value to the key class_{{$index}} just to that property gets added as a class name to the class list of the element. It is just the way you do active: checkForActive.
i.e
{active: checkForActive, disabled: checkForDisable, class_{{$index}} :true}
But i believe there could be some undesired behavior due the usage of interpolation ({{) within the ng-class directive (Atleast used to happen with older versions). So you could as well use an array.
ng-class="[checkForActive && 'active' , checkForDisable && 'disabled', 'class_' + $index]"
The above method will add a class name false if active or disabled is false, which should be harmless.
Or pass index to a controller function say getStatus($index) and return the object from there and use it in the ng-Class directive.
$scope.getClass = function(){
var obj = {active: $scope.checkForActive, disabled: $scope.checkForDisable};
obj['class_' + this.$index] = true;
return obj;
}
and
ng-class="getClass()"
#Okazari pointed out that it indeed works by mixing class with ng-class so you could also do:
class="class_{{$index}}" ng-class="{active: checkForActive, disabled: checkForDisable}"

Change class of just one element in an ngRepeat

I have an ng-repeat. And I would like to change the class of just that element vs the entire group.
<button ng-class="defaultClass" ng-repeat="tube in node.Tubes" ng-click="toggleBtn(tube)">{{"Tube " + ($index + 1)}}</button>
with the above HTML on my ng-click I can pass the tube, which really only gives me the data passed from the API, if I console.log(this) I see the class name in an element called $$watchers but that seems odd to change it from there.
$scope.toggleBtn = function (element) {
console.log(element);
console.log(this);
}
In my controller I have $scope.defaultClass = "btn btn-off"; but if I change that with the function it changes every element.
How can I only change the class of the element clicked?
Continuing from the comments. Than you can't use $scope variable for that, because, as you said, it will be the same. TO solve this you need to use ng-class properly.
Docs: https://docs.angularjs.org/api/ng/directive/ngClass (see an example at the bottom of the page)
<button ng-class="{'btn-on' : tube.toggled, 'btn-off' : !tube.toggled}" ng-repeat="tube in node.Tubes" ng-click="toggleBtn(tube)">{{"Tube " + ($index + 1)}}</button>
http://plnkr.co/edit/WKLJ45yRYu1583C2ZnfT?p=preview
I'm not sure I understand if that's what you want, but you can use:
<button class="btn" ng-repeat="tube in node.Tubes" ng-class="{'btn-off':!toggled, 'btn-on':toggled}" ng-click="toggled = !toggled">{{"Tube " + ($index + 1)}}</button>
no need to add any code to the controller in this case.
These other answers are excellent. However, I would like to point out another method for this. Let's say you were looping through an array of receipts. For example, in the controller you had receipts in scope:
$scope.receipts = [ ... ];
And on the front-end you were looping through these receipts:
<div ng-repeat="receipt in receipts">{{ receipt.[attribute] }}</div>
One way to keep track of unique classes is to add a "class" key to the receipt objects. For example a receipt would look like:
receipt { 'id': '1', 'class' : ' ... ' }
On the front-end, you would determine the class using ng-class:
<div ng-repeat="receipt in receipts">
<div ng-class="receipt.class"></div>
</div>
And then, you could pass the receipt and its ID to a toggleButton() method like so:
<div ng-repeat="receipt in receipts">
<div class="btn" ng-click="toggleClass(receipt.id)"></div>
<div ng-class="receipt.class"></div>
</div>
And then within the controller you could simply update the class for that particular receipt (assuming here there is a method getReceipt() that gets a receipt from the array $scope.receipts):
$scope.toggleClass = function(id) {
getReceipt(id).class = { ... }
}
This would dynamically change the class for that single receipt based on the logic within the toggleClass() function.

Resources