How to use a dynamic value with ngClass - angularjs

I'm trying to apply a class name that's the same as a scope variable.
For example:
<div ng-class="{item.name : item.name}">
So that the value of item.name is added to the class. This doesn't seem to do anything though. Any suggestions on how to do this?
Thanks!
EDIT:
This is actually being done within a select, using ng-options. For example:
<select ng-options="c.code as c.name for c in countries"></select>
Now, I want to apply a class name that has the value of c.code
I found the following directive, which seems to work, but not with interpolation of the value:
angular.module('directives.app').directive('optionsClass', ['$parse', function ($parse) {
'use strict';
return {
require: 'select',
link: function(scope, elem, attrs, ngSelect) {
// get the source for the items array that populates the select.
var optionsSourceStr = attrs.ngOptions.split(' ').pop(),
// use $parse to get a function from the options-class attribute
// that you can use to evaluate later.
getOptionsClass = $parse(attrs.optionsClass);
scope.$watch(optionsSourceStr, function(items) {
// when the options source changes loop through its items.
angular.forEach(items, function(item, index) {
// evaluate against the item to get a mapping object for
// for your classes.
var classes = getOptionsClass(item),
// also get the option you're going to need. This can be found
// by looking for the option with the appropriate index in the
// value attribute.
option = elem.find('option[value=' + index + ']');
// now loop through the key/value pairs in the mapping object
// and apply the classes that evaluated to be truthy.
angular.forEach(classes, function(add, className) {
if(add) {
angular.element(option).addClass(className);
}
});
});
});
}
};
}]);

Better later than never.
<div ng-class="{'{{item.name}}' : item.condition}">
yes. ' and {{ for classname.

I'm on angular 1.5.5 and none of these solutions worked for me.
It is possible to use the array and map syntax at once though it's only shown in the last example here
<div ng-class="[item.name, {'other-name' : item.condition}]">

Simply using the variable should be sufficient:
<div ng-class="item.name" />
This is also documented in the official documentation.

I think you missed the concept.
A conditional css class looks like this:
<div ng-class="{'<css_class_name>': <bool_condition>}">
And I dont think you want:
<div ng-class="{'true': true}">
You probally want to use:
<div ng-class="item.name"></div>

Angularjs Apply class with condition:
<div ng-class="{true:'class1',false:'class2'}[condition]" >

This can be useful in some cases:
HTML:
<div ng-class="getCssClass()"></div>
JS:
$scope.getCssClass = function () {
return { item.name: item.name };
};

Related

Is there an easy way to extend an attribute directive in angularjs?

I want to make a custom ng-if but can't find any good examples of how that should be done.
What I'm aiming for is:
<div my-if="someText">....</div>
I want that to expand to
<div ng-if="true|false">....</div>
where true|false depends on someText. Is there an easy way to do this?
To replace a directive with an ng-if a recompile is needed. The easiest way I get it working was this:
(function () {
angular.module('enheter').directive('toggleIf',['$compile', function($compile) {
return {
restrict: 'A',
compile: function(el,attr) {
var toggleName = attr.toggleIf;
var toggleOn = toggleName === "sometext";
el.attr('ng-if', toggleOn);
el.removeAttr('toggle-if');
var fn = $compile(el);
return function(scope) {
fn(scope);
};
}
};
}]);
})();
The directive above first get the value of the toggleIf attribute. The part that define the value of toggleOn will be more complex, but this shows what I was aiming for. Then I just add the ng-if to the element and remove the toggle-if. If the toggle-if was not removed there would be an infinite loop since the call to $compile would execute this function again and again.
<div ng-if="showText ? true : false">....</div>
but then again, if your showText is a boolean, it will do the same thing.

Change background color with ng-Style

How can i change the background color with ng-style?
this Div gonna repeat so the one of the color is from DB. For the plnkr i just fixed the colors, but in my example is like this:
<div class="col-md-offset-0 col-md-2" ng-repeat="type in types" style="margin-bottom:5px;">
<div class='container' divCheckbox ng-style="{'background-color':(isSelected?'{{type.color}}':'#ccc')}>
<input type='hidden' ng-model="type.show" />
<div class="label">{{type.name}}</div>
</div>
</div>
And the directive:
.directive('divCheckbox', function () {
return {
restrict: 'A',
link: function (scope, el, attr) {
scope.isSelected = el.find('input').val() == 'false';
el.on('click', function () {
scope.isSelected = !scope.isSelected;
scope.$apply();
});
}
}
});
Heres my plnkr: http://plnkr.co/edit/onLA8vSbtwQu1OxZrKzT?p=preview
You can't do ternary conditions within a tag and you have an error since you didn't quote background-color. You have to either quote it, or use camelCase, while the conditions should be set in the controller.
So, the fix is to have a scope variable denoting a color (or the full style object) and use it like this: http://plnkr.co/edit/iYkSa2I1ysZutdkAKkuh?p=preview
UPDATE
Here's an example you could use to make your code work with your DB (I'm calling external JSON here, but the principle is the same): http://plnkr.co/edit/Kegs95NNyGGySMDzhQed?p=preview
This way you could fetch the 'selected' color as well. That's pretty much all I can tell you with the info you provided.
Both are different styles. Use:
return {backgroundImage:'URL'};
or
return {backgroundColor:'Color'};
youse:
return {backgroundImage:'someimg'};

AngularJS - How to show custom directives based on condition

I have to show a custom directive (i.e. task-moveable) based on some condition. I have to only show the task-movable attribute for tasks which is not completed yet. Is there any way we can do this in Angularjs?
HTML Code:
<div class="gantt-body-background" ng-repeat="row in gantt.rows" task-moveable>
....
</div>
Thanks in advance.
You could make a tweak such that your taskMoveable directive can observe a value assigned to it. From there do an $eval on the value of the taskMoveable attribute to get your boolean.
As an example:
app.directive('taskMoveable', function () {
return {
controller: function ($scope, $element, $attrs) {
$scope.taskMoveable = {};
$attrs.$observe('taskMoveable', function (value) {
if (value) {
$scope.taskMoveable.amIMoveable = $scope.$eval(value);
}
});
},
template: '<span ng-bind="taskMoveable.amIMoveable"></span>'
};
});
See my plunk here for a more detailed example:
http://plnkr.co/edit/0nK4K9j3SmNnz8PgRYfR
You could use ng-if for that whole element. Something like this.
<div class="gantt-body-background" ng-repeat="row in gantt.rows" ng-if="thing.stuff" task-moveable>
....
</div>
Then that div would only be in the DOM if thing.stuff was truthy.

Conditionally setting the class on the option tag with AngularJS

I've been struggling with a problem for a few days. I'm trying to apply a class to an option tag based on a value in the model it is bound to. I tried using ng-Class as this post (How to use ng-class in select with ng-options) describes, but that didn't work. So then I tried using the directive from that post and that didn't work either. My problem seems to be in the expression as it is either always true or always false and never based on the value in the model. I'm not sure if it's something to do with how the expression is being handled by $parse. Here is the view:
<div class="col-md-2">
<div class="merchant-list">
<input type="checkbox"
ng-model="allMerchants"
ng-change="allMerchants_Changed()">All merchants<br />
<select size="10"
ng-model="overviewCtrl.currentMerchant"
ng-options="item.Id as item.Name for item in allMerchantData"
ng-disabled="allMerchants"
ng-change="currentMerchant_Changed()"
options-class="{ 'merchant-item-waiting':item.status=='w','merchant-item-error':item.status=='e','merchant-item-loaded':item.status=='l'}"
>
<option value="">--All Merchants--</option>
</select>
<p>Current Merchant: [{{ overviewCtrl.currentMerchant }}]</p>
</div>
</div>
Here is how I am setting the status on the model when the data is returned.
MerchantService.getAllMerchantData().query(function (response) {
// Add a status flag to the merchant. waiting, loaded, error
for (var merchant in response)
{
response[merchant].status = 'w';
}
$scope.allMerchantData = response;
SystemMetricService.loadSystemMetrics(response, $scope);
}, function (error) {
SharedService.logError("Error getting all merchant data", error);
});
For reference, here is the directive so you don't have to go to How to use ng-class in select with ng-options:
angular.module('app.directives')
.directive('optionsClass', function ($parse) {
return {
require: 'select',
link: function (scope, elem, attrs, ngSelect) {
// get the source for the items array that populates the select.
var optionsSourceStr = attrs.ngOptions.split(' ').pop(),
// use $parse to get a function from the options-class attribute
// that you can use to evaluate later.
getOptionsClass = $parse(attrs.optionsClass);
scope.$watch(optionsSourceStr, function (items) {
// when the options source changes loop through its items.
angular.forEach(items, function (item, index) {
// evaluate against the item to get a mapping object for
// for your classes.
var classes = getOptionsClass(item),
// also get the option you're going to need. This can be found
// by looking for the option with the appropriate index in the
// value attribute.
option = elem.find('option[value=' + index + ']');
// now loop through the key/value pairs in the mapping object
// and apply the classes that evaluated to be truthy.
angular.forEach(classes, function (add, className) {
if (add) {
angular.element(option).addClass(className);
}
});
});
});
}
};
});
As you suspect, it has to do with $parse.
You write
options-class="{ 'merchant-item-waiting':item.status=='w','merchant-item-error':item.status=='e','merchant-item-loaded':item.status=='l'
Notice how you reference item.status explicitly. Following the original example, you should just write status instead.
Now, why is this? Looking at the documentation for $parse (https://docs.angularjs.org/api/ng/service/$parse) we see this part:
Returns function(context, locals) - a function which represents the compiled expression:
context – {object} – an object against which any expressions embedded in the strings are evaluated against (typically a scope object).
In the directive we have the part:
var classes = getOptionsClass(item),
which means in this case, you can think of item (one of the options) as being scope-like, in the sense that you wouldn't write scope.status in the code, but just reference status.

inline conditionals in angular.js

I was wondering if there is a way in angular to conditionally display content other than using ng-show etc. For example in backbone.js I could do something with inline content in a template like:
<% if (myVar === "two") { %> show this<% } %>
but in angular, I seem to be limited to showing and hiding things wrapped in html tags
<p ng-hide="true">I'm hidden</p>
<p ng-show="true">I'm shown</p>
What is the recommended way in angular to conditionally show and hide inline content in angular just using {{}} rather than wrapping the content in html tags?
Angular 1.1.5 added support for ternary operators:
{{myVar === "two" ? "it's true" : "it's false"}}
EDIT: 2Toad's answer below is what you're looking for! Upvote that thing
If you're using Angular <= 1.1.4 then this answer will do:
One more answer for this. I'm posting a separate answer, because it's more of an "exact" attempt at a solution than a list of possible solutions:
Here's a filter that will do an "immediate if" (aka iif):
app.filter('iif', function () {
return function(input, trueValue, falseValue) {
return input ? trueValue : falseValue;
};
});
and can be used like this:
{{foo == "bar" | iif : "it's true" : "no, it's not"}}
Thousands of ways to skin this cat. I realize you're asking about between {{}} speifically, but for others that come here, I think it's worth showing some of the other options.
function on your $scope (IMO, this is your best bet in most scenarios):
app.controller('MyCtrl', function($scope) {
$scope.foo = 1;
$scope.showSomething = function(input) {
return input == 1 ? 'Foo' : 'Bar';
};
});
<span>{{showSomething(foo)}}</span>
ng-show and ng-hide of course:
<span ng-show="foo == 1">Foo</span><span ng-hide="foo == 1">Bar</span>
ngSwitch
<div ng-switch on="foo">
<span ng-switch-when="1">Foo</span>
<span ng-switch-when="2">Bar</span>
<span ng-switch-default>What?</span>
</div>
A custom filter as Bertrand suggested. (this is your best choice if you have to do the same thing over and over)
app.filter('myFilter', function() {
return function(input) {
return input == 1 ? 'Foo' : 'Bar';
}
}
{{foo | myFilter}}
Or A custom directive:
app.directive('myDirective', function() {
return {
restrict: 'E',
replace: true,
link: function(scope, elem, attrs) {
scope.$watch(attrs.value, function(v) {
elem.text(v == 1 ? 'Foo': 'Bar');
});
}
};
});
<my-directive value="foo"></my-directive>
Personally, in most cases I'd go with a function on my scope, it keeps the markup pretty clean, and it's quick and easy to implement. Unless, that is, you're going to be doing the same exact thing over and over again, in which case I'd go with Bertrand's suggestion and create a filter or possibly a directive, depending on the circumstances.
As always, the most important thing is that your solution is easy to maintain, and is hopefully testable. And that is going to depend completely on your specific situation.
I am using the following to conditionally set the class attr when ng-class can't be used (for example when styling SVG):
ng-attr-class="{{someBoolean && 'class-when-true' || 'class-when-false' }}"
The same approach should work for other attribute types.
(I think you need to be on latest unstable Angular to use ng-attr-, I'm currently on 1.1.4)
I have published an article on working with AngularJS+SVG that talks about this and related issues. http://www.codeproject.com/Articles/709340/Implementing-a-Flowchart-with-SVG-and-AngularJS
For checking a variable content and have a default text, you can use:
<span>{{myVar || 'Text'}}</span>
If I understood you well I think you have two ways of doing it.
First you could try ngSwitch and the second possible way would be creating you own filter. Probably ngSwitch is the right aproach but if you want to hide or show inline content just using {{}} filter is the way to go.
Here is a fiddle with a simple filter as an example.
<div ng-app="exapleOfFilter">
<div ng-controller="Ctrl">
<input ng-model="greeting" type="greeting">
<br><br>
<h1>{{greeting|isHello}}</h1>
</div>
</div>
angular.module('exapleOfFilter', []).
filter('isHello', function() {
return function(input) {
// conditional you want to apply
if (input === 'hello') {
return input;
}
return '';
}
});
function Ctrl($scope) {
$scope.greeting = 'hello';
}
Angular UI library has built-in directive ui-if for condition in template/Views upto angular ui 1.1.4
Example:
Support in Angular UI upto ui 1.1.4
<div ui-if="array.length>0"></div>
ng-if available in all the angular version after 1.1.4
<div ng-if="array.length>0"></div>
if you have any data in array variable then only the div will appear
if you want to display "None" when value is "0", you can use as:
<span> {{ $scope.amount === "0" ? $scope.amount : "None" }} </span>
or true false in angular js
<span> {{ $scope.amount === "0" ? "False" : "True" }} </span>
So with Angular 1.5.1 ( had existing app dependency on some other MEAN stack dependencies is why I'm not currently using 1.6.4 )
This works for me like the OP saying {{myVar === "two" ? "it's true" : "it's false"}}
{{vm.StateName === "AA" ? "ALL" : vm.StateName}}
Works even in exotic Situations:
<br ng-show="myCondition == true" />
I'll throw mine in the mix:
https://gist.github.com/btm1/6802312
this evaluates the if statement once and adds no watch listener BUT you can add an additional attribute to the element that has the set-if called wait-for="somedata.prop" and it will wait for that data or property to be set before evaluating the if statement once. that additional attribute can be very handy if you're waiting for data from an XHR request.
angular.module('setIf',[]).directive('setIf',function () {
return {
transclude: 'element',
priority: 1000,
terminal: true,
restrict: 'A',
compile: function (element, attr, linker) {
return function (scope, iterStartElement, attr) {
if(attr.waitFor) {
var wait = scope.$watch(attr.waitFor,function(nv,ov){
if(nv) {
build();
wait();
}
});
} else {
build();
}
function build() {
iterStartElement[0].doNotMove = true;
var expression = attr.setIf;
var value = scope.$eval(expression);
if (value) {
linker(scope, function (clone) {
iterStartElement.after(clone);
clone.removeAttr('set-if');
clone.removeAttr('wait-for');
});
}
}
};
}
};
});
More recent angular, 6+ I think. You have ng-template with the else conditional hooking up to a tag identifier:
<div *ngIf="myVar else myVarNo">Yes</div>
<ng-template #myVarNo><div>No</div></ng-template>

Resources