How to get row index in angular ui-grid 3.0 - angularjs

I'm working with angular ui-grid version 3.0 and can not find the way to get the index of the row, to add a numeration column to the grid.
I would like to help me.

There isn't a way to get the index of the row easily, so it depends what you're trying to do. What do you expect the numbering to do when someone sorts the data - do you want the numbers to stay as they were in the original data, or do you want them to change and align to the new sort order?
In the FAQ http://ui-grid.info/docs/#/tutorial/499_FAQ we find:
The question here is what you're really trying to achieve. Do you want the actual row index, or that you want to display a sequential id in all your rows?
If the latter, then you can do it by just adding a counter column to your data:
$scope.myData.forEach( function( row, index){
row.sequence = index;
});
If you want to show the index of the row within the grid internals, then it depends on which internal you want. You can get the index of the row within grid.rows, which would show the row as it stands in the original rows list (not filtered nor sorted), or the index of the row within grid.renderContainers.body.visibleRowCache (filtered and sorted), or the render index of the row within the currently displayed rows (given virtualisation, this is generally a particularly useless number).
If you're OK that whenever someone sorts or filters then the numbers will change, then you could do it with a cellTemplate, which would be something like:
cellTemplate: '<div class="ui-grid-cell-contents">{{grid.renderContainers.body.visibleRowCache.indexOf(row)}}</div>'

cellTemplate: '
{{rowRenderIndex + 1}}'

the problem of first solution is that it does not work properly whith pagination. the celltemplate of index column must be something like this to have the right index on each page and not to begin from 1 on each page :
{ field: 'index', displayName: 'Index', width: '50', cellTemplate: '<div class="ui-grid-cell-contents">{{grid.renderContainers.body.visibleRowCache.indexOf(row)+(grid.options.paginationPageSize*(grid.options.paginationCurrentPage-1))+1}}</div>' }
this solution would work even for client-side pagination or for server-side pagination

by using this way to solve this problem...
$http.get('./api/ioni_users')
.success(function(data) {
$scope.gridOptions.data = data;
angular.forEach(data, function(data, index) {
data["index"] = index+1;
//data.push({"index":index+1})
})
});

The following worked for me as I needed to see the index of the row as it related to the entire dataset not just what was visible to the user. It can be cleaned up but this is the raw code that I was able to get to work using the formula
((0/100)+((2*100)-100))+1 where
0 = rowIndex (Zero Based)
100 = pagination page size
2 = current page
+1 = because the array is zero based
cellTemplate: '<div class="ui-grid-cell-contents">{{(((grid.renderContainers.body.visibleRowCache.indexOf(row) / grid.options.paginationPageSize)*100)+((grid.options.paginationPageSize * grid.options.paginationCurrentPage) - grid.options.paginationPageSize)) + 1}} </div>'
Hope this helps.

Use rowRenderIndex in your cell template, it is the native variable available on each row
E.g
columnDefs: [
{
enableFiltering: false,
enableSorting: false,
field: "sn",
displayName: "Sn#",
cellTemplate:
"<div class=\"ui-grid-cell-contents\" >{{rowRenderIndex}} s</div>"
}
]

Related

How to update autoGroupColumnDef property of ag-Grid after table is initialized

I have an ag-grid table (Enterprise version: 22.1.0) which is grouped using autoGroupColumnDef property. The grouping is dependent on the table's data and the data loads on a button click. I need to update the autoGroupColumnDef property's field name (_this.colName in the below code) after the page is loaded, right before loading the data.
Table's grid options:
_this.gridOptions = {
defaultColDef: {
sortable: true,
resizable: true,
filter: true
},
columnDefs: _this.columnDefs,
rowData: [],
enableRangeSelection: true,
autoGroupColumnDef: {
headerName: "Sector",
field: _this.colName,
cellRendererParams: {
suppressCount: true
},
tooltipValueGetter: function(params) {
return _this.tooltipVal
}
},
suppressAggFuncInHeader: true,
enableBrowserTooltips: true
};
I update the variable _this.colName before setting data to the grid. I have tried the following options and none of them worked for me:
_this.gridOptions.api.refreshClientSideRowModel('group');
_this.gridOptions.api.refreshCells();
_this.gridOptions.autoGroupColumnDef.field = 'Column's Name'
Any help would be appreciated!
There is a good workaround for this. You can set autoGroupColumnDef, then remove and readd all row groupings. It will redraw the group column with the new name.
gridOptions.autoGroupColumnDef.headerName = 'new_name';
// Get current groupings
var colstate = gridOptions.columnApi.getColumnState();
var colstateclear = gridOptions.columnApi.getColumnState();
// Clear groupings
var x = 0, xcount = colstateclear.length;
while ( x < xcount ) {
colstateclear[x].rowGroupIndex = null;
x += 1;
}
gridOptions.columnApi.setColumnState(colstateclear);
// Reset groupings
gridOptions.columnApi.setColumnState(colstate);
I contacted ag-grid support and apparently this is a bug and they have it in their backlog with no ETA available for now. A workaround they provided was to use: https://www.ag-grid.com/javascript-grid-grouping/#showRowGroup.
This is not really a good workaround because the grouped columns are separated and makes the page feel cramped. Also there are some look and feel issues that keep popping up (Eg: empty space added before each column that increases with each grouped column. ie second column has 1 cm added before it, third column has 2 cm added before it and so on. I guess this was added to bring the grouped look in the group column but you wouldn't expect this behavior when the columns are separated.)
ag-grid's backlog ID for the ticket: AG-3359 - Allow the autoGroupColumn to be used in the API calls for columns, at the moment there is no way to dynamically change it after creation. (ie, setColumnDefs …)
Link to track the progress: https://www.ag-grid.com/ag-grid-pipeline/
there is a straight forward method to update the autoGroupColumnDef object and its properties with setAutoGroupColumnDef
this.gridOptions.api.setAutoGroupColumnDef(<ColDef>{
...this.gridOptions.autoGroupColumnDef, // preserve the other settings except the ones you need to change
minWidth: 500
})
if any problems with the spread operator,
do it manually:
this.gridOptions.api.setAutoGroupColumnDef(<ColDef>{
// ...this.gridOptions.autoGroupColumnDef, // preserve the other settings except the ones you need to change
headerName: this.gridOptions.autoGroupColumnDef.headerName,
minWidth: 500
})
and one more thing, add this if you have any visual bugs, like: header row gets resized but bellow rows stays the same as previus state, so the refresh of model is required:
this.gridOptions.api.refreshClientSideRowModel();
this refresh is not ideal solution, because it refreshes everything, so you will loose expanded levels for example, still no clue how to preserve all settings.
https://angulargrid.com/angular-grid/client-side-model/#refreshing-the-client-side-model
and best solution for now is tu use:
this.gridOptions.api.redrawRows();
it keeps the rows expanded if are, checkbox selected if is.

Ag-grid master detail prevent detail row from closing on data refresh

I'm currently doing a trial on AG-Grid master detail feature. Things are working fine but my data will be refreshed every 10 seconds. This caused the details to close when the data is refresh and I have to open the detail rows again.
Are there any options to save the state of the details that was opened?
Plunkr
Data is set to refresh every 5 seconds , expand the detail row and when the data refreshes the detail will be collapse. I've set rememberGroupStateWhenNewData : true
https://plnkr.co/edit/SgYD3vH8CXW9W9B8HD6N?p=preview
var gridOptions = {
rememberGroupStateWhenNewData:true,
columnDefs: columnDefs,
masterDetail: true,
detailCellRendererParams: {
detailGridOptions: {
rememberGroupStateWhenNewData:true,
columnDefs: [
{field: 'callId'},
{field: 'direction'},
{field: 'number'},
{field: 'duration', valueFormatter: "x.toLocaleString() + 's'"},
{field: 'switchCode'}
],
onFirstDataRendered(params) {
params.api.sizeColumnsToFit();
}
},
getDetailRowData: function (params) {
params.successCallback(params.data.callRecords);
}
},
onFirstDataRendered(params) {
params.api.sizeColumnsToFit();
}
};
A little late, but this may help others.. If you use Immutable data mode, and set the refresh mode of your detail to 'rows', your master and detail will update in-place.
Check these links for more info:
https://www.ag-grid.com/react-data-grid/immutable-data/
https://www.ag-grid.com/react-data-grid/master-detail-refresh/
The problem is that you're using api.setRowData to update the data.
https://www.ag-grid.com/javascript-grid-data-update/
This is the simplest of the update methods. When you call
api.setRowData(newData), the grid discards all previous selections and
filters, and completely overwrites the old data with the new. This was
the first way the grid worked and is the most 'brute force' way.
Use this method if you want to load the grid with a brand new set of
data.
This description does not match what you're trying to do, so you should use one of the other methods. Try api.updateRowData(transaction), there are plenty of examples for it in the demos.
Did you try rememberGroupStateWhenNewData?
https://www.ag-grid.com/javascript-grid-grouping/#keeping-group-state
have the same issue here, rememberGroupStateWhenNewData only works on row grouping, not master/detail grids.

Angular UI-Grid: How to get number of total items after filtering

I am using UI Grid to display some data. One of the columns is text so I have 'contains' filtering which works perfectly.
I am also using pagination. The right corner of the UI-Grid shows something like:
1 - 23 of 23 items
In my page functionality (angular controller side), I need to return the number of total items, specifically the last "23" from that line. I could not find anything in the documentation other than this (from the docs):
GridOptions (api in module ui.grid.pagination )
totalItems
Total number of items, set automatically when client side pagination, needs
set by user for server side pagination"
So I tried using $scope.gridOptions.totalItems but unfortunately it always returns 0 when the page first loads.
My workaround was using data.length which would give me what I needed. After further testing though I realized that after you use the filtering, the total items on the pagination footer changes to the sum of the matching results. I have not found another way to get that number.
One more thing:
Is there an event that fires after filtering is complete so that I can check $scope.gridOptions.totalItems then?
Any ideas?
Thanks in advance :)
You should avoid jQuery (as another post suggests) and interact with the API instead.
You first need to save a reference to the API on the grids creation event.
$scope.gridOptions = {
....
onRegisterApi: registerGridApi,
....
};
function registerGridApi(gridApi) {
$scope.gridApi = gridApi;
}
You should already know the total number of rows.
You can get the number of visible/filtered rows with:
gridApi.core.getVisibleRows().length
or
gridApi.grid.getVisibleRows().length
You can get the number of selected rows with:
gridApi.selection.getSelectedRows().length
$scope.gridOptions = {
....
onRegisterApi: registerGridApi,
....
};
function registerGridApi(gridApi) {
$scope.gridApi = gridApi;
}
Get your total items :
var totalItems = $scope.gridApi.grid.options.totalItems;
You can use this workaround.
var RowsVisible = $(".ui-grid-row").length;
If grouping is there then use this
var RowsVisible = $(".ui-grid-row").length/2;
This will give you how many rows present. If grouping is there, then it will give you the the number of headings and the visible rows.
getVisibleRows() will return the number of visible rows. In case if You expand grouping, the size of getVisibleRows() will increase accordingly.
$scope.gridApi.core.getVisibleRows().length;
Therefore above approach has some limitations.
You can also get total grouped rows by using following approach.
var totalGroupedRows = Object.keys($scope.gridApi.grid.grouping.groupingHeaderCache).length ;
Note Object.keys() will not work IE<9.
In our application, we are showing some of the columns in the footer. It was not working with the column filter. We then did the below steps, then it worked.
i.$scope.gridOptions = {
columnDefs: $scope.columnDefs,
onRegisterApi: function(gridApi) {
$scope.gridApi = gridApi;
}
ii. calling below template in the footer
footerCellTemplate: '<div class="footer-class" style="text-align:right;margin-right: 5px;" >{{grid.appScope.getTotalofcolumn(grid) | currency:number:0}}</div>',
iii. inside getTotalofcolumn used below line for rows
$scope.gridApi.core.getVisibleRows();
$scope.totalValues =$scope.gridApi.core.getVisibleRows();
$scope.Total = 0
angular.forEach($scope.totalValues,function(value,key){
$scope.Total += value.entity.TotalCOLUMNVALUE/NAME;
});
return $scope.Total;

How to filter my combined column? (ng-grid)

I have an ng-grid which one of his column is a combined column:
angular.forEach($scope.opportunitiesData,function(row){
row.getABDisplayName = function(){
return this.a + ' - ' + this.b;
};
});
$scope.gridOptions = {
data: 'loadGridItems',
headerRowHeight: 0,
multiSelect: false,
showFilter: false,
columnDefs: [{field:'getABDisplayName()',displayName:"Tasks"},
...],
enableSorting: true,
filterOptions: $scope.filterOptions
};
my problem is that the filter doesn't work on that field.
I tried to change it, and break down the combined field to 2 fields and the filter works...
not sure what is the difference, as both fields are strings.
I have been watching this as I had the same problem (only with three concatenated field for the column to filter on). Eventually I came up with the following:
You'll need to create your own filter, and bypass the ng-grid's built-in filterOptions altogether.
Something along the lines of:
// pass in your original dataset to filter, and filter text
app.filter('opportunitiesFilter',function(){
return function(array, input){
var match = []; // will hold your newly filtered data
if (input = '') return array; // return orig data if nothing entered
// use foreach to loop through your original array
angular.forEach(array, function(item){
// your filter logic goes here - might be the following:
if (item.indexOf(input) >= 0) match.push(item);
});
return match;
});
And down in your controller code, add a watch to your search text field and call the filter when it changes. You'll pass in your original data array and search field text to the filter, and bind the resulting filtered array to your ng-grid data param. Be sure to include the $filter call in your controller signature. ie. function($scope, $http, $filter, ...){ }
// watch your input field for changes to filter on, call custom filter, and bind to grid
$scope.watch('searchText'), function(){
$scope.'loadGridItems' = $filter('opportunitiesFilter')($scope.opportunitiesData, $scope.searchText);
}
This worked for me, although you may need to tweak your filter as needed - like using .toLowerCase() etc to do case-insensitive matching and the like.
Hope this helps you out - it took me awhile but now I'm good to go.
Cheers,
Dan

Showing a limited subset of (or individual record from) a store in an Ext.DataView

In Sencha Touch, I often need to have an Ext.DataView panel that contains a small sub-set records or even a single record from the collection in the store.
For example I might have a Model for Car which has thousands of car records in it's app.stores.cars store but I want to show a smaller subset of these items (say; just sports cars) in my listOfSportsCars DataView while also showing the larger complete set of cars in my listOfCars DataView.
My first thought was to use multiple stores. So I'd have one main store for the big list of all cars, and a second store with a filter for my subset of sportscars. However, now updating a model from one store does not automatically update the record in the other store, so this defeats the purpose of using a DataView as the changes are not updated everywhere in the page when updating records.
My second attempt was to overwrite the collectData method on the DataView, which sounded exactly like what I was after:
var card = new Ext.DataView({
store: app.stores.cars,
collectData: function(records, startIndex){
// map over the records and collect just the ones we want
var r = [];
for( var i=0; i<records.length; i++ )
if( records[i].data.is_sports_car )
r.push( this.prepareData(records[i].data, 0, records[i]) );
return r;
},
tpl: new Ext.XTemplate([
'<tpl for=".">',
'<div class="car">{name}</div>',
'</tpl>'
]),
itemSelector: 'div.car'
});
A full example can be found here.
But, although it's documented that I can/should override this method, Sencha Touch really doesn't like it when you mess around with the length of the array returned by collectData so this was a dead-end.
How do others deal with displaying/updating multiple collections of the same records?
UPDATE There was a bug preventing collectData from working as expected. The bug has since been fixed in Sencha Touch 1.1.0.
As written in the comment:
I've used your democode with the last Sencha Touch release and opened all with Google Chrome. In the current version the error is fixed. (Version 1.1)
you could use Filters in order to get a subset of the data asociated to that store.
yourstore.filter('name', 'Joseph');
Also you should define 'root' as a function so it will always return an array. Readers in sencha touch asume you're always going to get an array as response, but it's not true if you are having a JSON with a single entry, try something like this:
root: function(data) {
if (data) {
if (data instanceof Array) {
return data;
} else {
return [data];
}
}
The full code for the store could be like this:
YourApp.ViewName = new Ext.data.Store({
model: 'YourApp.models.something',
proxy: {
type: 'scripttag',
url: 'http://somerandomurl/service/json',
extraParams: {
param1: 'hello'
},
reader: {
type: 'json',
root: function(data) {
if (data) {
if (data instanceof Array) {
return data;
} else {
return [data];
}
}
}
}
},
});
Hope it helps.
I use the "filter" features in the Store. Not modifying the DataView (I use a List).
Here's a snippet where I will fiter out Programs with a catagory that fit's a regex. (I have Programs with a catagory field)
MyApp.stores.Programs.filter(function(object) {
var regex = new RegExp(filterValue, 'i');
return object.data.category.search(regex) >= 0; // found match
});
You can clear the filter like this:
MyApp.stores.Programs.clearFilter(false);
This will update the DataView (I use a List) immediately (it's amazing).
So within your filter you could just filter out sports cars, or cars of a certain price, or whatever.
Hope that helps...
For my understanding of Sencha Touch this is not the best approach.
If it can be still good for performance you shoud use a second "slave" store, with inline data (http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Store) that you can populate automatically from main store with subset of information you want to show when an event occours on the master store, i.e. load event.
If you want to deal with just one store a solution I can imagine is to use an xtemplate with "tpl if" tag in the dataview where you want to show just some information
http://docs.sencha.com/touch/1-1/#!/api/Ext. to write empty records. Maybe, also better solution, could be to use a custom filter function inside xtemplate, in order to put a css with visibility hidden on the items you don't want to see.

Resources