How can one apply conditional grouping in an Angular ng-repeat directive?
For example, in the below code, I want to groupBy:'var1', but only when some variable, or expression, dontGroup evaluates to true. I don't want to have the bottom tr row duplicated specifically for the dontGroup variable in my HTML because the information cannot be loaded in via a template for a second tr due to the main table being controlled by a directive and needing the template in the DOM before initialization. If there's a way to do this that I'm overlooking, please let me know. I'm utilizing ng-table.
<table>
<tr ng-repeat-start="(key, data) in $data | groupBy:'var1'">
<td ng-bind="key">
<!-- Grouped header info here. -->
</td>
</tr>
<tr ng-repeat-end ng-repeat="item in data"> <!-- Repeat $data when `dontGroup` evaluates to true,
otherwise repeat pre-grouped data from the `groupBy` filter above -->
<td ng-bind="item.var2">
<!-- Individual item info here. -->
</td>
</tr>
</table>
So, I want the lower ng-repeat function to repeat $data as data only when dontGroup evaluates to true, but otherwise I want $data to be run through the groupBy filter such that the data can have a header for the group.
I also cannot just drop an ng-if on the header row when dontGroup evaluates to true because I need the data to sort by other variables such as var2, var3, etc, and not have the var1 grouping affect that.
A fiddle for you to play with showing both wanted functionalities.
You can set the groupBy to reference a function and in the controller you can do a custom groupBy that will check for an indicator of how to group. Please see the following docs for more information.
http://ng-table.com/#/grouping/demo-api
Here is a rough attempt which should show the concept.
http://jsfiddle.net/wwgo4eo4/
//template
<input type="checkbox" ng-click="clickGroupBY()"/>
<table>
<tr ng-repeat-start="(key, data) in $data | groupBy:valueGroupBY">
//controller
$scope.willgroupby = true;
$scope.valueGroupBY = function(group) {
if($scope.willgroupby) {
return group.var1
}
return group.var2
}
$scope.clickGroupBY = function clickGroupBY() {
$scope.willgroupby = !$scope.willgroupby;
}
UPDATE
I ran with the solution you came back with here and cleaned it up. see the following: http://jsfiddle.net/r1mss7s0/1/
I also found this to be informative about the filters you are using:
https://github.com/a8m/angular-filter/wiki/Common-Questions
Here I'm setting a conditional on the header row and returning a dummy object when dontGroup evaluates to true. It does mean, however, that var1 can't be equal to "test" in the grouped array, else the header won't be shown. "test" will just be a protected value.
Further, you could use this syntax with other filters when off of the grouping, as shown below.
<table>
<tr ng-repeat-start="(key, data) in (dontGroup ? {'test': $data} : ($data | groupBy:'var1'))" ng-if="key != 'test'">
<td ng-bind="key">
</td>
</tr>
<tr ng-repeat-end ng-repeat="item in data | orderBy: 'var2')">
<td ng-bind="item.var2">
</td>
</tr>
</table>
Fiddle
Check John Babb's answer for offloading some of this logic into the controller!
Related
I'm building a table that is populated by ng-repeat going through a list that comes from the database. This part works fine, but I want to have a checkbox that lets the user select which rows are deleted. I'd also like to have a checkbox on the table header that works as a select all button.
The problem is that, as I understand it, the ng-repeat loop includes everything inside the row in question leaving me unable to insert a manual cell between the loop and the end of the row.
Is there a way to add an additional cell between the loop and the end of the row, for example by using ng-repeat-start and ng-repeat end?
Here is an example of my attempts. This one did not work, obviously.
<table>
<tr><th ng-repeat=" e in poistoavaimet">{{e}}</th></tr>
<tr ng-repeat="i in poistolista | filter:sukunimiVal | filter:etunimiVal">
<td ng-repeat-start=" key in poistoavaimet">{{i[key]}}</td>
<td ng-repeat-end>
<input type="checkbox" ng-model="poistovalinta.{{$index}}">
{{$index}}</input>
</td>
</tr>
</table>
poistoavaimet contains the extracted keys from the array. Poistolista is the array itself from which the keys are extracted and it contains the data from the database. Poistovalinta is a variable for the values of the checkboxes. poistovalinta.{{$index}} was an attempt to create an entry in poistovalinta -array that has the key of the index of the row.
I'll add a snippet of the code responssible for handling these variables from the controller:
$scope.haePoistettavat = function(){
$http.get('/opiskelijahaku', {params: {'optio':2}})
.then(function(res){
$scope.poistolista = res.data.message;
$scope.poistoavaimet = Object.keys($scope.poistolista[0]);
$scope.adminLista = {};
$scope.adminLista['poisto'] = true;
}, function(error){
console.log(error)
});
}
This function sends a http get to the server with an option that tells the server to return the correct list. The response is stored in $scope.poistolista and the keys are extracted in to the array $scope.poistoavaimet. $scope.adminLista contains true/false values which link to ng-show in multiple objects. The idea is to only show one of them at the time. This is not directly linked to the question.
Thank you #RaphaMex for clearing up my misunderstanding about how ng-repeat works!
The solution is exactly what he said. Just add the cell after the repeat and it works.
Here is the fixed version:
<table>
<tr><th ng-repeat=" e in poistoavaimet">{{e}}</th><th><input id="kaikki" type="checkbox" ng-model="kaikkiVal"></th></tr>
<tr ng-repeat="i in poistolista | filter:sukunimiVal | filter:etunimiVal">
<td ng-repeat=" key in poistoavaimet">{{i[key]}}</td>
<td><input type="checkbox" ng-model="poistovalinta"></td>
</tr>
</table>
I want to add sorting functionality on date header, I added orderBy filter but it's breaking the column.
How can I add sorting to date header? I would like for it to be possible for the user to click the header, which should change the sorting order.
main.html
<table>
<tr>
<th>File</th>
<th>{{date | orderBy: file.fileStat}}</th>
<th>Download</th>
</tr>
<tr ng-repeat="file in data">
<td>{{file.filename}}</td>
<td>{{file.fileStat |date : "dd.MM.y"}}</td>
<td><button type="button" class="btn btn-primary" ng-click="downloadServerFile(file.filename)">download</button></td>
</tr>
</table>
A few errors in the syntax you've attempted, but you've made a good start.
The first error is that the orderBy: applies only to the ng-repeat directive.
Let's first change <th>{{date}}</th>.
Next, let's look at ng-repeat. There's two things which are of use to us. See: OrderBy.
So we can tell it, using a String, which field to sort on, and with a boolean, whether to reverse the order or not. We can try something such <tr ng-repeat="file in data | orderBy: 'fileStat': false">.
We should also set the boolean to a variable, such that we can keep track of when the user changes it.
<tr ng-repeat="file in data | orderBy: 'fileStat': reversed">
Finally, let's have an ng-click, to allow the user to change the order. In this example, I am going to set it on the <th>.
<th ng-click="reversed = !reversed">{{date}}</th>
Please try this and let me know if you run into any errors.
I have created an application with ngTable using grouping functionality, The application is working fine but the problem is that when I add dynamic data (rows) to the table, its not reflecting dynamically, unless or otherwise when we click the table title for sorting or when we click the pagination
I have recreated the problem within a plunker, there you can find a button, when clicked one Dynamic row is added but not reflecting within the table
PLUNKER
<body ng-app="main" ng-controller="DemoCtrl">
<button ng-click="addDynamicDatas()">Add Datas</button>
<table ng-table="tableParamsOne" class="table">
<tbody ng-repeat="group in $groups">
<tr class="ng-table-group" ng-hide="group.data[0].role==='None'">
<td>
<strong>{{ group.value }}</strong>
</td>
</tr>
<tr ng-repeat="user in group.data">
<td sortable="name" data-title="'Name'">
<span ng-class="{'bold-text': user.role=='None'}" ng-show="user.role==='None'"> {{user.name}}</span>
</td>
<td sortable="age" data-title="'Age'">
{{user.age}}
</td>
</tr>
</tbody>
</table>
</body>
add function
$scope.addDynamicDatas = function()
{
$scope.myDataOne.push({name: "Abcd", age: 10, role: 'Administrator'});
}
Can anyone please tell me some solution for this?
This is probably not an ideal solution but is the only one that I could find.
You can add $scope.tableParamsOne.reload(); after you update your array.
Also currently when your grid is updating when you click a header it is not updating the amount of pages in the pagination. To solve this you can add $scope.tableParamsOne.total($scope.myDataOne.length);
I am also using ng-table(#4.0.0) to make local/client-side insertion/deletion after a successful server submit and got the similar problem of update ng-table. #Bradley's answer above does not work directly. So I chased the source code and found that the reload() called the getData(). After insert a row into the target rowset (ctrl.tbContents.rows in my case), I have to add bellow line in mytbParams definition to make ctrl.tbParams.reload() work:
getData: function() {
return ngTableDefaultGetData(ctrl.tbContents.rows, ctrl.tbParams);
}
Note to inject the ngTableDefaultGetData.
I start to play with angularjs, and having this data model :
friends = [
{name:'John**amine**allo'},
{name:'Mary**aaa**mmm'},
{name:'Mike**bb**kkk'}
]
I would like to split each name by '**' to create a table of names.
I don't know if there is more straightforward way to do this ( maybe using some filter in angular-filter module) but here what I have so far tried:
<table id="searchObjResults">
<tr><th>Name</th></tr>
<tr ng-repeat="x in friends">
<td ng-repeat="y in x.split('**')">
{{y}}
</td>
</tr>
thanks in advance for any help.
You need to split name property if the x elements:
<td ng-repeat="y in x.name.split('**')">{{y}}</td>
However this is probably not very good idea what you are trying to do. First of all, you need to take care of proper table structure (correct header colspan values). Secondary, it's better to render already processed data structure, rather then converting values inside templates. It just makes it overcomplicated, not to mention makes watch expression slower.
Demo: http://plnkr.co/edit/5PXTW1gjpKwg7e3S6hRM?p=preview
You can use a custom filter to specify the separator:
<table id="searchObjResults">
<tr><th>Name</th></tr>
<tr ng-repeat="x in friends">
<td ng-repeat="y in x.name | mysplit:'**'">
{{y}}
</td>
</tr>
</table>
JS:
app.filter('mysplit', function() {
return function(data, sep) {
return data.split(sep);
}
});
Plunker
I just stepped into the AngularJS world, and need a solution for the application I am working on.
So here is the definition of the module.
var samplesApp = angular.module('samples', []);
samplesApp.controller('samplesController', function ($scope) {
var jsonObj=
[
{"ACTION":"UPDATE","ID":"22","ROUTE":"0015"},
{"ACTION":"DELETE","ID":"20","ROUTE":"0015"},
{"ACTION":"UPDATE","ID":"19","ROUTE":"0015"}
]
$scope.records = jsonObj;
var columnNames = [];
for (var key in jsonObj[0]) {
columnNames.push(key);
}
$scope.columnNames = columnNames;
});
The $scope JSON object is a part of the output of the real data that comes from database. and I need to put these data into a table dynamically
The html is like
<table>
<thead>
<th data-ng-repeat="column in columnNames">{{column}}</th>
</thead>
<tr data-ng-repeat="record in records">
<td data-ng-repeat="column in columnNames">{{ record.{{ column }} }}</td>
</tr>
</table>
Because I have no idea what the column name is, so I made a process to get all the column names and push them into $scope.columnNames. and then bind it to the table header. There is no problem for this part. The issue is I don't know how to get the value coresponse to the specific column. I was trying to do it like this:
<td data-ng-repeat="column in columnNames">{{ record.{{ column }} }}</td>
But apparently it is not working.
Can someone give me some advice? really appreciate it.
Try using brackets, like this:
<tr data-ng-repeat="record in AllRecords">
<td data-ng-repeat="column in columnNames">{{ record[column] }}</td>
</tr>
The expressions inside {{ }} (moustaches?) are evaluated pretty narrowly to how regular javascript is evaluated.
Accessing with the record[column] operator is OK, but
you could try this other alternative. In this example, the semantics
of the code is a bit better, and you have less coupling (your rows just
depend on the data, not the variable used in the header):
<table>
<tr>
<th data-ng-repeat="column in columnNames">{{column}}</th>
</tr>
<tr data-ng-repeat="record in records">
<td data-ng-repeat="(key,value) in record">{{value}}</td>
</tr>
</table>
Here's the working example with your data: http://plnkr.co/edit/CskLQ2ZZlYIURdNPXiZt?p=preview
Here's the Angular docs for the ngRepeat directive (look for "key, value")