ng-show does not update in a ng-repeat loop - angularjs

I have some hierarchical data (some kind of a tree view) that represents an organization chart. I want to show this in a table. I did the following:
<tr ng-repeat="dat in vm.refined" ng-show="dat.show || dat.parent==null">
<td ng-click="vm.toggleShow(dat)" style="cursor: pointer">
<span ng-class="(dat.show===true || dat.parent===null) ? 'glyphicon glyphicon-chevron-right' : 'glyphicon glyphicon-chevron-down'" ></span>
{{dat.value.rbsName}}
</td>
<td>{{dat.parent}}</td> <!-- ignore these two tds -->
<td>{{dat.members}}</td> <!-- ignore these two tds -->
</tr>
vm.refined is an array of objects, defined in the controller which has the name of the unit, its parent (if any, else null) and a show flag to simulate the collapse/expand.
toggleShow() responsibility is to... toggle the show flag in the selected (which will be shown anyway due to being parent) and to its children. So I do something like:
vm.toggleShow=function(dat) {
dat.show=!dat.show;
for(var i=0; i< vm.refined.length; i++) {
if(vm.refined[i].parent==dat.parent)
vm.refined[i].show=!vm.refined[i].show;
}
};
Thing is that when I click the td, toggleShow is called but I will not see the rows I am supposed to see. It seems like a scope issue and ng-repeat but I cannot find a solution around. Can you help? Oh and something else, do you feel it would be better if I write this part as a directive?

Basically you are toggling function is doing nothing. At the start of the function it is toggling flag & then you are looping through the array & when it found you are toggling that flag. By doing that your value is true the it became true only, Nothing will be change in value.
I think you should toggle that flag on html itself
Markup
<tr ng-repeat="dat in vm.refined" ng-show="dat.show || dat.parent">
<td ng-click="dat.show=!dat.show" style="cursor: pointer">
<span ng-class="(dat.show || dat.parent) ? 'glyphicon glyphicon-chevron-right' : 'glyphicon glyphicon-chevron-down'" ></span>
{{dat.value.rbsName}}
</td>
<td>{{dat.parent}}</td> <!-- ignore these two tds -->
<td>{{dat.members}}</td> <!-- ignore these two tds -->
</tr>

Related

ng repeat filter on dynamically created variable

Previously I've used a trick to dynamically generate a variable like this:
<div ng-click="showHide=(showHide ? false : true)">toggle bool</div>
<header ng-show="showHide"></header>
The code above will generate a local variable called showHide that I can use on ng-* attributes. In this case, when the div is clicked, the header is hidden.
Is there a way to create a similar solution for ng-repeat's filter, without having to add functionality to the controller (like in the example above)?
In the example below I have a timeline with a bunch of items. I've also got a statistics table over what items exist in this timeline. I want to be able to filter these items by category in the same approach as i did with the showHide example.
This is a snippet of my code:
<!-- stat table -->
<tr ng-repeat="type in $ctrl.typeOfCategory">
<td ng-click="filterCategory=(filterCategory ? type.name : '')">{{type.name}}</td>
</tr>
<!-- timeline -->
<div class="timeline-item" ng-repeat="msg in $ctrl.msgs | filter: filterCategory">
<div class="category">{{msg.type}}</div>
</div>
So, whenever i click an item in the table, i want the timeline to filter all the other items out. In my mind, filterCategoryshould now contain the value of what {{type.name}} is, and filter everything else out.
Is something like this achievable in angular, or am I stretching this technique to its limits?
Thanks.
ng-repeat creates is own scope, You should define filterCategory in controller scope i.e. $ctrl.filterString and then use it..
<!-- stat table -->
<tr ng-repeat="type in $ctrl.typeOfCategory">
<td ng-click="$ctrl.filterString=((type.name == $ctrl.filterString) ? '' : type.name)">{{type.name}}</td>
</tr>
<!-- timeline -->
<div class="timeline-item" ng-repeat="msg in $ctrl.msgs | filter: {type : $ctrl.filterString}">
<div class="category">{{msg.type}}</div>
</div>

Angular.js ng-repeat unique identifier

I am using ng-repeat on a <tr> tag to populate the <td> tags with data pulled from mysql and converted into Json. This works just fine. However, one of the <td> tags that I'm using contains a button.
What I would like to do, is have each of these buttons identified somehow in the DOM, so that I can target then with specific requests.
Example: Page loads, ng-repeat repeats a button 4 times. Each of these buttons would have an ng-click attached to it. I want each of them to open and filter different information in a json file.
Am I correct in assuming that ng-repeat would simply open the same item for each button, and how would I go about making them seperate? thanks.
You can do something like this on the front-end:
<button ng-repeat="item in items track by $index" ng-click="someFunction($index)" >Something happens</button>
Then in your controller:
$scope.someFunction = function (index) {
if (index === 1):
// etc.
else...
// Or use switch, whichever works for you.
You could create the specific function on each item in the array.
<button ng-repeat="button in buttons" ng-click="button.functionName()">{{button.name}}</function>
There's $index for that. It's a very good habit to take for any of your ng-repeat. Also don't forget bind-once if your buttons UI isn't subject to modifications once the DOM has loaded.
<ul>
<li ng-repeat="page in pages">
<a ng-class="{ testClass: $index == pageNumber }" ng-click="setPageNumber($index)">{{ page }} - index is : {{$index}}</a>
</li>
</ul>
http://jsfiddle.net/bonatoc/4z1t4gsm/3/
Also you could do (using bind-once):
<button
ng-repeat="button in ::buttons track by $index"
id="button_{{$index}}"
class="{{button['css_class']}}"
...given your buttons were a JSON object as well (beware, ng-repeat likes arrays, not objects. Arrays of objects are fine):
$scope.buttons = [
0: {
'css_class': someClass,
'functionToTrigger': function(...
// ...

Hide html element with same data in ng-repeat in angularjs

I have a array of task in which there are two duplicate departments, but i dont want to show the second duplicate record in ng-repeat, but only for the first record i want to show even if it is duplicate.
Here is my code, can anyone tell me where i'm going wrong.
<tr ng-repeat="t in item.requisitionTask track by t.id">
<td>
<div ng-hide="!$first ? item.requisitionTask[$index-1].department.departmentCode==$index.department.departmentCode : false">
{{t.department.departmentName}}</div>
</td>
Solved it by targeting the data particularly.
<td><div ng-hide="!$first ? item.requisitionTask[$index-1].department.departmentCode==item.requisitionTask[$index].department.departmentCode : false">
I suspect that you want to hide data if the previous item contains the same departmentCode as your current item. As mentioned in my comment, you should move this logic into a function on your controller's scope.
<tr ng-repeat="t in item.requisitionTask track by t.id">
<td>
<div ng-hide="isNotFirstOrSameCodeAsPrevious($index)">
{{t.department.departmentName}}
</div>
</td>
</tr>
In your controller:
function isNotFirstOrSameCodeAsPrevious($index) {
if ($index === 0) return false;
return item.requisitionTask[$index - 1].department.departmentCode ===
item.requisitionTask[$index].department.departmentCode;
}

Ng-click does not work outside of my tr tag AngularJS

I am working on an app and I cant seem to figure out why my ng-click only works inside of my (single) tr tag but as soon as I put it into another tr tag it stop working. Keep in mind it was working before I used the ng-repeat within the first tr tag. Here is what my code looks like, any advice would greatly help!
<table>
<tbody>
<tr>
<td ng-click="commentOpen = !commentOpen">
<div class="iconsize">Comment Closed</div>
</td>
<td ng-click="switchOpen = !switchOpen">
<div class="iconsize">Switch Closed</div>
</td>
</tr>
<tr>
<td>
<div ng-show="commentOpen == true">
<textarea>Comment Open</textarea>
</div>
<div ng-show="switchOpen == true">
<p>Switch On</p>
</div>
</td>
</tr>
</tbody>
</table>
I had the ng-repeat on the tag which was causing my ng-click to not fire. I ended up moving my ng-repeat to the tbody and the ng-click and ng-show started working again.
ngRepeat creates new scopes for its children, it just usually seems like it's accessing the same scope because this new scope inherits from its parent.
Meaning, commentOpen is actually referring to a property on the wrong scope.
Below are three potential ways for you to fix this:
1) controller as, and always refer to the controller you're after by name
2) $parent.commentOpen (Don't do this! It becomes very confusing as you nest)
3) Instead of commentOpen and switchOpen, you can use an Object (e.g. $scope.openControls = { comment: false, switch: false }, and then in the td tags you would write something like ng-click='openControls.comment = !openControls.comment'). This way it's passed inherited by reference (where as a boolean would be by value), and keeps synced.

angular: how to make a button disabled based on a condition

Sorry for not being specific, but I'm having a blackout and am desperate.
Have been struggling for 2 days now to find the logic to do the following:
every item has a 'inCart' value. if it's 0, it's not in there, if it's below 7, it's in one of 6 'slots' depending on the value.
now i have buttons that assign each item to a value in inCart.
but if one of the items has a value in it that is 4, then I obviously don't want all the items to show a button that will assign it to 4.
<tr ng-repeat="x in myItems">
<a ng-repeat="slot in [1,2,3,4,5,6]" ng-click="addToCart(x._id, slot)"
class="btn btn-primary {{x.inCart != '0' ? 'disabled' : ''}}">
{{slot}}
</a></tr>
Thinking about it logically I know my mistake: i only look if the current item is in the cart, and if it is then it disables the button. How do I look at all the items in the cart, and if one of them is for example 4, then all the buttons that assign an a value 4 to inCart are disabled, so that I won't have multiple items with the same location inCart.
based on the approach I can fix this I am pretty sure I can make everything work better then how it is now, it's just that I can't even figure this out so let alone the more detailed issues.
You can use the ngClass directive
You can evaluate a expression with ng-class and if it's true you can apply a certain class. If not, then the class won’t be applied.
<button ng-class="{'disabled': expression}">
If you use an actual button, you can make use of ngDisabled:
<input type="button" ng-repeat="slot in [1,2,3,4,5,6]"
ng-click="addToCart(x._id, slot)"
class="btn btn-primary"
ng-disabled="x.inCart !== '0'" value="{{slot}}" />
You could call a function to check if the slot is available.
Controller
// return the slots already used
var slotsUsed = $scope.myItems.map(function (i) {return i.inCart;}).filter( function(s) { return s > 0;});
$scope.slotAvailable = function(s) {
return slotsUsed.indexOf(s) == -1;
}
View
<tr ng-repeat="x in myItems">
<td>
<a ng-repeat="slot in [1,2,3,4,5,6]"
class="btn btn-primary"
ng-click="addToCart(x._id, slot)"
ng-class="{'disabled': x.inCart != '0' || !slotAvailable(slot)}">
{{slot}}
</a>
</td>
</tr>
There's a directive designed specifically for this: ngDisabled
https://docs.angularjs.org/api/ng/directive/ngDisabled

Resources