Angular show/hide with nested repeats - javascript

I have a double repeat going on here, an initial repeat list, and then a second one (for expanding details). What is the best way to show/hide the second repeat ONLY at that index in which clicked at. I have them separated by class name however Angular's Jquery Lite doesn't support the "nextUntil" feature. I'm thinking a ng-class conditional but I don't want based on the Scope (needs to be temp for each expand).
<tr ng-repeat-start="x in calls track by $index" class="{{x.status}}" class="{{x.status}}" ng-click="getCallDetails(x,$index)" my-draggable >
<td><small><i class="fa fa-plus"></i></small></td>
<td style="text-align:center;"><span class="label label-default stat-context {{x.status}}">{{x.statusdesc}}</span></td>
<td>{{x.cust_no}}</td>
<td>{{x.company}}</td>
<td>{{x.name}}</td>
<td>{{x.emp_id}}</td>
<td>{{x.enterdate}}</td>
</tr>
<tr class="callDetails" ng-class="callDetails" ng-repeat-end ng-repeat="y in x.callDetails" >
<td></td>
<td colspan="2">{{y.emp_name}}</td>
<td>{{y.attempt_date}}</td>
<td colspan="2">{{y.note}}</td>
<td class="allAtt-stat">{{y.callstatus}}</td>
</tr>
</tbody>
my simple angular functions
$scope.getCalls = function() {
$http.get("callView.php")
.success(function(response) {
$scope.calls = response;
});
};
$scope.getCallDetails = function(attempt,ind) {
$http.get("callDetails.php?a=" + attempt.action_id)
.success(function (response) {
attempt.callDetails = response;
});
};

With nested ng-repeats, $index would refer to the innermost scope.
If you want to differentiate between parentindex and childindex, child elements can be accessed using $index while parent elements can be accessed either using $parent.$index or using ng-init, parentindex can be initialised to some value.
First approach:
<div ng-repeat="item in items">
<div>{{item.key}}</div>
<ul ng-repeat="val in item.value track by $index">
<li >child index {{$index}} -- parentIndex {{$parent.$index}}</li>
</ul>
</div>
Second approach:
<div ng-repeat="item in items" ng-init="parentIndex=$index">
<div>{{item.key}}</div>
<ul ng-repeat="val in item.value track by $index">
<li >child index {{$index}} -- parentIndex {{parentIndex}}</li>
</ul>
</div>
see the demo for reference

Related

Change HTML in Angular 7 by reference

I'm passing UL by reference in an Angular function and I want to append some 'li' in that UL.
<ng-container *ngIf="FileUploaded">
<tr class="" *ngFor="let row of this.fileJson">
<td class="text-left">{{row.Comment}}</td>
<td class="text-center">
<div class="tags">
<ul class="tags tags--ds ui-sortable" #forAddingTag> // This is the reference which is passed in below button's function named "AddTag()"
<ng-container *ngFor="let key of this.Object.keys(row)">
<li #ToRemoveTag class="tags__item" *ngIf="key.substring(0,5)=='Topic'">
<p>{{row[key]}}</p> <span class="close__item"><i class="fal fa-times"
(click)="removeTag(ToRemoveTag)"></i></span>
</li>
</ng-container>
</ul>
</div>
</td>
<td class="text-center">
<a class="btn" (mousedown)="AddTag(forAddingTag)">Update</a> // Here the reference is being passed
</td>
</tr>
</ng-container>
Here is the typescript code
AddTag(evalue){
debugger
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
}
var value = text;
}
I just want to add some more "li" at the end of this UL. I can do it with the help of JQuery but it shouldn't be used in Angular.
I'd recommend not to run any function in the *ngFor tag. It'll be called too many times than you think it does. Better way would be to create another object in the controller (something like filteredRows) and loop over it in the template with the keyvalue pipe.
Template
<ng-container *ngIf="FileUploaded">
<tr class="" *ngFor="let row of filteredRows; let rowIndex=index">
<td class="text-left">{{ row.comment }}</td>
<td class="text-center">
<div class="tags">
<ul class="tags tags--ds ui-sortable" #forAddingTag>
<ng-container *ngFor="let topic of filteredRows.topics | keyvalue; let topicIndex=index">
<li #ToRemoveTag class="tags__item">
<p>{{ topic.value }}</p> <span class="close__item"><i class="fal fa-times"(click)="removeTag(rowIndex, topicIndex)"></i></span>
</li>
</ng-container>
</ul>
</div>
</td>
<td class="text-center">
<a class="btn" (mousedown)="addTag(rowIndex)">Update</a>
</td>
</tr>
</ng-container>
Then you could modify the filteredRows variable (add/remove properties) to reflect the changes in the template.
Controller
filteredRows: {comment: any, topics: any[]}[];
ngOnInit() {
fileJson.forEach(row => {
const comment = row['Comment'];
const topics = JSON.parse(JSON.stringify(row)); // <-- create a deep copy of `row` object
for (const key in topics) {
if (topics.hasOwnProperty(key)) {
if (key.substring(0, 5) !== 'Topic') {
delete topics[key];
}
}
}
this.filteredRows.push({comment: comment, topics: topics});
});
}
addTag(index) {
this.filteredRows[index].topics.push(
{
// object content
}
);
}
removeTag(rowIndex, topicIndex) {
delete this.filteredRows[rowIndex].topics[topicIndex];
}
Function name AddTag() goes against the usual convention of camel case naming for functions.

AngularJS - Target specific element inside ng-repeat

I´m using ng-repeat to iterate an array and create a table. In that table, I have a button to make a download. When I click the download button I want the link to disappear and make a loading spin appear. The problem is, the spin is showing up in all the rows and not in just the one i click.
Html -
<tbody md-body>
<tr md-row ng-repeat="">
<td md-cell>
<div layout="row" layout-align="center center">
<md-progress-circular ng-if="isSubmit"></md-progress-circular>
<a ng-if="!isSubmit" ng-click="download($index)">Download</a>
</div>
</div>
</td>
</tr>
</tbody>
JS -
$scope.download = function(index) {
angular.forEach($scope.downloads, function (download) {
// I can console log the index i click
console.log(index)
});
}
You should use the $index on the ng-repeat and instead a boolean isSubmit, use the index to compare to array index.
HTML
<tr ng-repeat="item in items">
<td>
<md-progress-circular ng-if="isLoadingIndex == $index"></md-progress-circular>
<a ng-if="isLoadingIndex != $index"
ng-click="download($index)">Download</a>
</td>
</tr>
CTRL
$scope.isLoadingIndex = null;
$scope.donwload = function($index) {
$scope.isLoadingIndex = $index;
//Rest of your code...
}

ng-if and ng-class-even/odd doesn't work well together and won't get expected outout

Here,I am loading a json file and using ng-repeat I display json data into table form.I am adding following features to code.
checkboxes to add the two CSS:
Bubble CSS
text-danger [In built class]
when you click on those check boxes, the CSS is applied only to the even rows in the table.
Using the ng-if or ngHide/Show to display the User-info which have the gender : Male.
I use ng-if to fulfill condition because ng-if will remove elements from DOM. This means that all your handlers or anything else attached to those elements will be lost.
ng-show/ng-hide does not remove the elements from DOM. It uses CSS styles to hide/show elements.
I created Codepen demo for my problem.Here ng-if and ng-class-even doesn't give expected o/p.
HTML:
<body ng-app="module1" ng-controller="module1Ctrl as flight">
<div class="page-header">
<h1>Angular app</h1>
</div>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="danger">
Something's Wrong
</label>
<label>
<input type="checkbox" ng-model="bubble">
Zoom In/Out
</label>
</div>
<table class="table table-striped table-bordered">
<tr>
<th>name</th>
<th>Birthdate</th>
<th>gender</th>
<th>father</th>
<th>mother</th>
</tr>
<tr ng-repeat="x in flightData"
ng-class-even ="{'text-danger':danger,'bubble': bubble}"
ng-if="x.sex=='f'">
<td>{{x.name}}</td>
<td>{{ x.born |date:'yyyy-MM-dd THH:mm:ss'}}</td>
<td>{{x.sex }}</td>
<td>{{x.father}}</td>
<td>{{x.mother}}</td>
</tr>
</table>
</div>
</body>
JS
var app=angular.module('module1',[]);
app.controller('module1Ctrl', function ($scope,$http) {
$scope.getDataFromServer = function () {
$http.get('http://codepen.io/timivey/pen/emQZYY.js').then(
function(res){
console.log(res.data);
$scope.flightData = res.data;
}
,function (err) {
console.log(err.message);
})
};
$scope.getDataFromServer();
$scope.showList='table';
});
Just a small change to Joel's answer. You no need to create a separate filter method.
<tr ng-repeat="x in flightData | filter:{sex:'f'}"
ng-class-even ="{'text-danger':danger,'bubble': bubble}">
There is nothing wrong with your code, but there is a small error with your understanding:
<tr ng-repeat="x in flightData"
ng-class-even ="{'text-danger':danger,'bubble': bubble}"
ng-if="x.sex=='f'">
The ng-if="x.sex == 'f' statement is omitting each <td> from display, but it does not remove them from the flightData array. ng-repeat is run for each item in the array, not for each item that is displayed.
If you add {{$index}} to each row of the table you will see that the order is not 0,1,2,3... it is 1,2,7,11,14...
If you remove the ng-if statement, you will see that every even row has correct class applied.
Instead of using ng-if to remove the males from the list, use a filter instead:
<tr ng-repeat="x in flightData | females"
ng-class-even ="{'text-danger':danger,'bubble': bubble}">
Append this filter to your controller statement in the JS code:
.filter('females', function() {
return function(input) {
var out = [];
angular.forEach(input, function(x) {
if(x.sex == 'f') {
out.push(x);
}
});
return out;
};
})
http://codepen.io/anon/pen/mABJyo
What this does is remove the males from the list before it runs the ng-repeat and will correctly output in the order 0,1,2,3...

How Can I show only selected div elements based on a certain condition in angularjs

So this is the snippet of my code, the aim here is to either hide or show the h4 element based on my condition.
<h4 ng-hide="data_startupcosts.note == ' '">THE COST OF STARTING THIS VENTURE</h4>
<div ng-repeat="data_startupcosts in data.startupcosts track by $index">
{{data_startupcosts.note}}
<ul ng-repeat="data_startupcosts_lists in data_startupcosts.list track by $index">
<li>
I used ng-hide with an expression but no luck...... any suggestions?????
You use the data_startupcosts outside your ng-repeat, so that variable won't exist in there. Instead use it inside your ng-repeat scope:
<div ng-repeat="data_startupcosts in data.startupcosts track by $index">
<h4 ng-hide="data_startupcosts.note == ' '">THE COST OF STARTING THIS VENTURE</h4>
{{ data_startupcosts.note }}
<ul ng-repeat="data_startupcosts_lists in data_startupcosts.list track by $index">
<li>..</li>
</ul>
</div>
Or, if you really want it outside the div, use ng-repeat-start:
<h4 ng-repeat-start="data_startupcosts in data.startupcosts track by $index"
ng-hide="data_startupcosts.note == ' '">THE COST OF STARTING THIS VENTURE</h4>
<div ng-repeat-end>
{{ data_startupcosts.note }}
<ul ng-repeat="data_startupcosts_lists in data_startupcosts.list track by $index">
<li>..</li>
</ul>
</div>
Also make sure your condition is correct, you are comparing with a single space, but maybe there isn't any space? (data_startupcosts.note == '')

get $index of item within ngrepeat

<ul ng-repeat="item in items">
<li>{{item.name}}</li>
</ul>
js
$scope.item = [
{
"itemId": 1,
"name":"laptop"
}
];
I want to the $index of item to be my itemId so in the future I can store in db or use it in somewhere else. I can do <li>{{$index}}</li> but how to bind it with my object of item in js?
You can bind with
<li ng-bind="$inndex" ></li>
Or store in id or other tags by
<li custom_tag="{{$index}}"></li>
One trick for selection of individual items of ng-repeat, is the inclusion of an controller in the same element as the ng-repeat.
The ng-repeat dedicated controller will contain all the new items generated by the iteration, ng-click calling a function in that controller will do the item selection.
Check Demo here.
<tbody ng-repeat="i in invoices"
ng-controller="RowCtrl"
ng-click="toggleRow()"
ng-switch on="isSelected()">
<tr>
<td>{{i.name}}</td>
<td>{{i.company}}</td>
</tr>
<tr ng-switch-when="true">
<td>{{i.invoice}}</td>
<td>{{i.units}}</td>
</tr>

Categories

Resources