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}"
Related
I have a function that sets a property in an object to false if it's true and true if it's false. Yet every time the function runs the object doesn't get affected.
$scope.menuButtons = [{header: "beaded", isActive: false},
{header: "laced", isActive: false}
]
$scope.activeButton = function(isActive) {
isActive == true ? isActive = false : isActive = true;
};
Here's the HTML
<div ng-repeat="b in menuButtons">
<div ng-click="activeButton(b.isActive)" class="shop-navbar-button">{{b.header}}</div>
</div>
I'm using the isActive value to see if the button is active so I can use a different class. For some reason the isActive value in the $scope doesn't get affected.
Pass in b instead of b.isActive. The boolean is just being passed by value, so re-assigning the value will have no effect on b.isActive.
I think you can change from this:
<div ng-click="activeButton(b.isActive)" class="shop-navbar-button">{{b.header}}</div>
to this:
<div ng-click="b.isActive=!b.isActive" class="shop-navbar-button">{{b.header}}</div>
You will NO need the auxiliar function. It is cleaner and more angular way.
I'm trying to use ng-class with a condition, like this:
<li ng-repeat="time in matter.times_hourly | filter:searchText" ng-class="{'time.write_off_class' : time.write_off === true, 'time.narrativeState' : time.write_off === false}" >
time.write_off_class has two classes inside it called "write_off_bg time_closed".
time.narrativestate has one class inside it called "time_closed"
time.write_off is a boolean.
So, I think my problem are the quotation marks.
I'm not sure where to put them, so I tried every possibility:
Quotes on condition:
`ng-class="{time.write_off_class : 'time.write_off === true', time.narrativeState : 'time.write_off === false'}"`
Result: Angular error.
`angular.min.js:107 `Error: [$parse:syntax] http://errors.angularjs.org/1.4.5/$parse/syntax?p0=.&p1=is%20unexpected%2C%…20%3D%3D%20true'%2C%20time.narrativeState%20%3A%20'time.write_off%20%3D%3DNaNalse'%7D&p4=.write_off_class%20%3A%20'time.write_off%20%3D%3D%20true'%2C%20time.narrativeState%20%3A%20'time.write_off%20%3D%3D%false'%7D
No quotes:
ng-class="{time.write_off_class : time.write_off === true, time.narrativeState : time.write_off === false}"
Result: Angular Error.
angular.min.js:107 Error: [$parse:syntax] http://errors.angularjs.org/1.4.5/$parse/syntax?p0=.&p1=is%20unexpected%2C%…f%20%3D%3D%20true%2C%20time.narrativeState%20%3A%20time.write_off%20%3D%3DNaNalse%7D&p4=.write_off_class%20%3A%20time.write_off%20%3D%3D%20true%2C%20time.narrativeState%20%3A%20time.write_off%20%3D%3D%false%7D
Quotes on everything (class and condition):
ng-class="{'time.write_off_class' : 'time.write_off === true', 'time.narrativeState' : 'time.write_off === false'}
Result: No error, but the element gets both classes, write_off_class AND narrativeState.
Quotes on classes :
ng-class="{'time.write_off_class' : time.write_off === true, 'time.narrativeState' : time.write_off === false}
Result: No error, and apparently the element gets the correct class (in this case, narrativeState, because at the beginning of the code all time.write_off are set to FALSE) BUT the element gets no style. If i put just ng-class="time.narrativeState" everything's ok, but if it gets it through the ng-class, then it is not working.
What am I doing wrong? Is there any reason for the element not styling through a condition with 'time.narrativeState' even when I'm sure it works by itself?
I've been trying more things and I know where the main problem is now, still cannot solve it.
My object 'time' has two fields inside that I use to give styling classes to some elements.
"time.narrativeState" has, for example, the class "time_closed" on it.
The thing is that, when I write ng-class="time.narrativeState" and I go to see the element's style, I can see "time_closed" BUT if I use instead the condition I was talking about in this question ng-class="{{ time.write_off ? time.write_off_class : time.narrativeState }}" > what the element gets as style is not "time_closed" it is literally "time.narrativeState" and because time.narrativeState is not a class but a object's field, it dosen't work.
Why it is getting "time.narrativeState" as the class and it's not looking INSIDE time.narrativestate to get the correct class "time_closed"???
You can use ternary operator inside simple interpolation tags:
ng-class="{{ time.write_off ? time.write_off_class : time.narrativeState }}"
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>
I have a table that lists customers and for customers that also have a client list allows the user to click the table row to show that list. The HTML looks like this:
<tbody ng-repeat ="item in customers | clientSettingsFilter:customerId">
<tr ng-class="{'row-hover': item.clientSettings.length>0}" ng-click="item.showClients=!item.showClients">
<td><span ng-class="{true:'glyphicon glyphicon-chevron-down', false:'glyphicon glyphicon-chevron-right'}[item.showClients]" ng-hide="item.clientSettings.length==0"></span></td>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td><button type="button" class="btn btn-default btn-xs" ng-click="go('/client-config/defaults/'+item.id)">Defaults</button></td>
</tr>
<tr ng-show="item.showClients">
..... // client data
The bizarre behavior I'm having is this:
If I leave the 'showClients' property undefined in the customers data set, everything works as expected, except that the chevron icon does not show at first. After clicking once, it shows up and the toggle works as expected. I was thinking this might be because the ng-class is looking for true or false, and undefined doesn't satisfy either of these.
If I pre-define the showClients property to either true or false, the chevron shows correctly on page load and the client list shows correctly, but the toggle no longer functions, as though ng-click is either not doing anything or for some reason is unable to change the value. I'm not sure how to debug an in-line directive like that.
EDIT
Per request, here is the relevent code from the controller:
filter('clientSettingsFilter', function () {
return function (customers, customerId) {
var filtered = [];
if (!customerId)
return filtered;
customerId = customerId.toLowerCase();
angular.forEach(customers, function (item) {
if (item.id.toLowerCase().indexOf(customerId) !== -1) {
// format some text properties, omitted for brevity
// if this line is uncommented, the ng-click does not work
//item.showClients = false;
filtered.push(item);
}
});
return filtered;
};
});
The conditional you are using in ng-class will only add something when value is either true or false, not when it's undefined.
Instead use the more verbose ternary operator:
ng-class="item.showClients ? 'glyphicon-chevron-down' : 'glyphicon-chevron-right'"
And might as well move the class glyphicon to the ordinary class attribute:
class="glyphicon"
Demo: http://plnkr.co/edit/bxgp4HyFkOygc0foxAKN?p=preview
The behavior you are witnessing when uncommenting item.showClients = false; in your filter is due to how the digest loop works.
If item.showClients is false and you click the tr the following will happen (a bit simplified):
The expression in ng-click will execute, setting item.showClients to true
The digest loop will start
The filter will run and set item.showClients to false again
Filters are meant for filtering, not for modification.
Also note that when using a filter with ng-repeat it will fire each digest cycle, and as each digest loop consists of multiple digest cycles (minimum of two) it's important to keep filters simple or they will have a bad impact on performance.
How can I count element by class name in angularJs?
I have tried with:
$scope.numItems = function() {
$window.document.getElementsByClassName("yellow").length;
};
Plunkr: http://plnkr.co/edit/ndCqpZaALfOEiYieepcn?p=preview
You have defined your function correctly, but made a mistake in showing its results: it should have been...
<p>{{numItems()}}</p>
... instead of plain {{numItems}}. You want to display the return value of the function, and not the function itself (that's meaningless), that's why you should follow the standard JS syntax for a function invocation.
Note that you can send arguments into this expression too: for example, I've rewritten that method like this:
$scope.numItems = function(className) {
return $window.document.getElementsByClassName(className).length;
};
... and then made three different counters in the template:
<p>Yellow: {{numItems('yellow')}}</p>
<p>Green: {{numItems('green')}}</p>
<p>Red: {{numItems('red')}}</p>
Plunker Demo.
But here's the real problem: numItems() result, used in one View, is based on DOM traversal - in other words, on another View. Not only that goes against Angular philosophy in general, it tends to break. In fact, it DOES break since this commit, as old as 1.3.0:
Now, even when the ngAnimate module is not used, if $rootScope is in
the midst of a digest, class manipulation is deferred. This helps
reduce jank in browsers such as IE11.
See, changes in classes are applied after digest - and that's after numItems() is evaluated, hence the delay in demo mentioned by #Thirumalaimurugan.
A quick-and-dirty solution is using another attribute for selector in numItems (in this plunker, it's data-color). But I would strongly advise you against it. The proper approach would be adding the data rendered by numItems() -using component into the model. For example:
app.js
// ...
var colorScheme = {
'toggle': {true: 'yellow', false: 'red'},
'toggle2': {true: 'green', false: 'red'},
'toggle3': {true: 'green', false: 'red'},
'toggle4': {true: 'red', false: 'green'}
};
$scope.getColor = function getColor(param) {
return colorScheme[param][$scope[param]];
};
$scope.countColor = function(color) {
return Object.keys(colorScheme).filter(function(key) {
return colorScheme[key][$scope[key]] === color;
}).length;
};
index.html
<p ng-class="getColor('toggle')">{{name}}</p>
<!-- ... -->
<p ng-repeat="color in ['Yellow', 'Green', 'Red']"
ng-bind="color + ' ' + countColor(color.toLowerCase())">
Demo.