ng-class not working with ng-repeat - angularjs

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.

Related

How to set a default value for AngularJS ng-repeat?

<li ng-repeat="item in quantityList track by $index" ng-model="isPageValid='true'">
<input value="item.quantity" ng-blur="validateQuantity(item)" ng-model="item.quantity">
</li>
I'm trying to set a default value for a status variable each time an ng-repeat loop occurs. I've tried ng-model, but that doesn't work. For instance,
I'd like to set isPageValid="true" before each time the ng-repeat loop runs. 'True' is will be the default value, and the validation function will test whether isPageValid should be set to 'false'.
I'd like the ng-repeat loop to run each time the ng-blur is exercised.
NOTE: I understand the way I'm using ng-model is incorrect, but this is just to illustrate the issue.
HTML:
<li ng-repeat="item in quantityList track by $index" ng-model="isPageValid='true'">
<input value="item.quantity" ng-blur="validateQuantity(item)" ng-model="item.quantity">
</li>
JS:
scope.validateQuantity = function(item){
var qty = item.quantity;
if(parseInt(qty) >=1 && parseInt(qty) <= 200){
item.isQuantityValid = true;
}else{
item.isQuantityValid = false;
scope.isPageValid = false;
}
}
The loop creates a list of input boxes. The objective is to create a global validation value called isPageValid which is 'false' if the validation by the JS fails for any input box. Note, when ng-blur is exercised, the JS validation runs and loop should re-run.
I believe ng-init could help...
<ul ng-init="isPageValid='true'">
<li ng-repeat="item in quantityList track by $index" >
<input ng-blur="validateQuantity(item)" ng-model="item.quantity">
</li>
</ul>
Note that it would be better practice to initialise isPageValid in the controller that is in the same scope as your validateQuantity function.
Here is an example of how you could initialise isPageValid in your controller and update the value after each call to validateQuantity...
In your controller:
scope.isPageValid = true;
function updatePageValid() {
scope.isPageValid = scope.quantityList.every(item => item.isQuantityValid);
}
scope.validateQuantity = function(item) {
var qty = parseInt(item.quantity);
item.isQuantityValid = (qty >= 1 && qty <= 200);
updatePageValid();
});

ngRepeat doesn't refresh rendered value

I'm having an issue with ngRepeat :
I want to display a list of students in two different ways. In the first one they are filtered by group, and in the second they are not filtered.
The whole display being quite complex, I use a ngInclude with a template to display each student. I can switch between view by changing bClasseVue, each switch being followed by a $scope.$apply().
<div ng-if="currentCours.classesOfGroup !== undefined"
ng-show="bClassesVue">
<div ng-repeat="group in currentCours.classesOfGroup">
<br>
<h2>Classe : [[group.name]]</h2>
<div class="list-view">
<div class="twelve cell"
ng-repeat="eleve in group.eleves | orderBy:'lastName'"
ng-include="'liste_eleves.html'">
</div>
</div>
</div>
</div>
<div class="list-view" ng-show="!bClassesVue">
<div class="twelve cell"
ng-repeat="eleve in currentCours.eleves.all"
ng-include="'liste_eleves.html'">
</div>
</div>
My problem happens when my list of students change (currentCours here). Instead of refreshing the ngRepeat, both lists concatenate, but only in the unfiltered view.
I tried adding some $scope.$apply in strategic places (and I synchronize my list for example) but it doesn't help.
EDIT : the function used to refresh currentCours in the controller. It's called when a "cours" is selected inside a menu.
$scope.selectCours = function (cours) {
$scope.bClassesVue = false;
$scope.currentCours = cours;
$scope.currentCours.eleves.sync().then(() => {
if ($scope.currentCours.classe.type_groupe === 1) {
let _elevesByGroup = _.groupBy($scope.currentCours.eleves.all, function (oEleve) {
return oEleve.className;
});
$scope.currentCours.classesOfGroup = [];
for(let group in _elevesByGroup) {
$scope.currentCours.classesOfGroup.push({
name: group,
eleves: _elevesByGroup[group]
});
}
$scope.bClassesVue = true;
}
});
utils.safeApply($scope);
};
Well, I found a workaround, but I still don't know why it didn't work, so if someone could write an explanation, I would be very thankful.
My solution was simply to open and close the template each time I switch between views.

ng-class multiple conditions

I would like to apply multiple conditions into ng-class so the upper limit on 'u.Credit' will be let say 10000. Here is my code:
<tr ng-repeat="u in user.tables.tbl1" ng-init="u.DateAdded = JSONtoDATE(u.DateAdded)" ng-class='{lastRow : $last}' ng-model="myIndex = $index">
<td>{{u.ID}}</td>
<td>{{u.Name}}</td>
<td>{{u.Email}}</td>
<td>{{u.DateAdded}}</td>
<td ng-class='{selectedRow : u.Credit >= creditValue}'>{{u.Credit}}</td>
</tr>
... I have tried something like this but it didn't work:
<td ng-class='{selectedRow : ((u.Credit >= creditValue) && (u.Credit < 10000))}'>{{u.Credit}}</td>
You can replace that with a function call on your controller:
<td ng-class='getClassForCurrentRow(u)'>{{u.Credit}}</td>
I'm not sure what creditValue is and from where is populated, if it's from the controller then you don't need a new parameter, if not then add it to the function getClassForCurrentRow(u, creditValue).
And in your controller define the class:
{...}
$scope.getClassForCurrentRow = function(record) {
if (record.Credit >= $scope.creditValue) {
return "selectedRow";
}
return "";
}
Be careful to leave this function as simple as possible, because Angular runs it on every cycle to determine if something changed.
<a href="" ng-click="addFavorite(myfav.id);favorite=!favorite">
<i class="fa orange" ng-class="{'fa-star': favorite || fav==myfav.id, 'fa-star-o': !favorite}"></i>
My apologize guys, I have made little mistake in tested code (I'm JS beginner) which had negative impact on desired functionality and after correcting it I have to say that expression I have claimed as 'not-working' is actually working:
selectedRow : (u.Credit >= creditValue && u.Credit < 10000)
However, your answers are very valuable to me as I'm learner. Thanks.
For multiple conditions in ng-class its better to create a function in controller that returns true or false for selectedRows in ng-class. like below:
{...}
$scope.getSelectedRow = function(record, creditValue) {
if ( record.Credit >= creditValue && record.Credit < 10000) {
return true;
}
return false;
}
Call this like:
<td ng-class='getSelectedRow(u,creditValue)'>{{u.Credit}}</td>
Try like below..
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<style>
.selectedRow{
color:#987789;
font-size:30px;
}
.unSelectedRow{
color:#234567;
font-size:30px;
}
</style>
<body ng-app="" ng-init="Credit = 10;creditValue=9;">
<p ng-class="{true: 'selectedRow', false: 'inactive'}[Credit >= creditValue]">Value : {{Credit}}</p>
<p ng-class="{false: 'unSelectedRow'}[Credit < creditValue]">Value : {{Credit}}</p>
</body>

AngularJS - how to highlight/add class to a max value

Let say I have a very simple data that Im looping through with ng-repeat.
Now, how can I highlight (add a css class) to a highest value in the data.
Data:
$scope.marks = [
{point:'11'},
{point:'2'},
{point:'23'}
];
html:
<ul>
<li ng-repeat="value in marks" class={{here add class for value 23}}> //how to add class to li with max value, in this case it is 23
{{ value.point}}
</li>
</ul>
Many thanks for your help
PLUNKR
You can use calculate the max value in a watch:
$scope.$watchCollection('marks', function(items) {
$scope.maxPoint = -1; // assuming we won't have negative values
angular.forEach(items, function(item) {
if (parseInt(item.point) > $scope.maxPoint) {
$scope.maxPoint = item.point;
}
});
});
Then use ngClass directive in your markup:
<li ng-repeat="value in marks" ng-class="{max : value.point == maxPoint}">
{{ value.point}}
</li>
Like Petr pointed out this won't perform well. Calculating the max value on changes is a better way. Here's a working plunk : http://plnkr.co/edit/F8yjoWR0ZWVF4tcck1rH?p=preview

How Do I Move Objects Inside An ng-Repeat on Button Click?

I have a nifty list of items in an ng-repeat with an up and down button on each. I just want the up button to move the list item up one place and the down button should move it down one place.
The problem is that I get an error saying "Cannot read property 'NaN' of undefined."
It seems "position" is undefined on the second line. What can I do to fix that?
Heres the javascript I'm working with (thanks to Rishul Matta):
$scope.moveUp = function(ind, position) {
$scope.temp = $scope.list[position - 1];
$scope.list[position - 1] = $scope.list[position];
$scope.list[position = temp];
};
Here's my HTML:
<ul>
<li class="steps" ng-repeat="step in selectedWorkflow.Steps track by $index" ng-class="{'words' : step.Id != selectedStep.Id, 'selectedWords' : step.Id == selectedStep.Id}" ng-model="selectedWorkflow.Step" ng-click="selectStep(step, $index); toggleShow('showSubStep'); toggleShow('showEditBtn')">
{{step.Name}}
<input class="orderUpBtn" type="button" ng-click="moveUp($index, step)" style="z-index:50" value="U" />
<input class="orderDownBtn" type="button" style="z-index:50" value="D" />
</li>
</ul>
Thanks!
Thanks for posting this question (+1) and the answer jtrussell (+1). I wanted to share what I believe to be a more re-usable/modular answer for other folks (inspired by odetocode.com post).
For the HTML, jtrussell's code is perfect because he fixed/simplified everything. For a better user experience I just added ng-disabled for the first/last elements.
HTML:
<ul ng-controller="DemoCtrl as demo">
<li ng-repeat="item in demo.list">
{{item}}
<button class="move-up"
ng-click="listItemUp($index)"
ng-disabled="$first">
Move Up
</button>
<button class="move-down"
ng-click="listItemDown($index)"
ng-disabled="$last">
Move Down
</button>
</li>
</ul>
For the JS, Notice the moveItem() function which I believe to be more re-usable. You can use this function for other drag+drop swapping functionality as well.
JS within Controller (tested on Angular 1.3.15):
// Move list items up or down or swap
$scope.moveItem = function (origin, destination) {
var temp = $scope.list[destination];
$scope.list[destination] = $scope.list[origin];
$scope.list[origin] = temp;
};
// Move list item Up
$scope.listItemUp = function (itemIndex) {
$scope.moveItem(itemIndex, itemIndex - 1);
};
// Move list item Down
$scope.listItemDown = function (itemIndex) {
$scope.moveItem(itemIndex, itemIndex + 1);
};
I hope it is helpful to someone out there. Thanks SO community!
A simple list with up/down buttons is pretty straightforward, here's some rough generic code. The ngRepeat directive will honor the order of items in your array so moving things around the view is just a matter of moving them in the array itself.
view:
<ul ng-controller="DemoCtrl as demo">
<li ng-repeat="item in demo.list">
{{item}}
<button ng-click="demo.moveUp($index)">up</button>
<button ng-click="demo.moveDown($index)">down</button>
</li>
</ul>
controller:
app.controller('DemoCtrl', function() {
this.list = list = ['one', 'two', 'three', 'four'];
this.moveUp = function(ix) {
if(ix > -1 && ix < list.length - 1) {
var tmp = list[ix+1];
list[ix+1] = list[ix];
list[ix] = tmp;
}
};
this.moveDown = function(ix) {
// similar...
};
});
There were a few strange items in your code (for example did you mean $scope.list[position] = temp; when you wrote ($scope.list[position = temp];), my example isn't perfect but it should get you going on the right path. Here's the full working demo: http://jsbin.com/vatekodeje, note that in my code I use "up" to mean increasing index rather than toward the top of the page.
Also in your controller you use position as an index (it's not clear that it should be) and make reference to, presumably, an array called $scope.list when in your view you use selectedWorkflow.Steps. Maybe your $scope.list and selectedWorkflow.Steps are meant to be the same thing?

Resources