Preserve selection in angular ui-grid while updating data - angularjs

http://plnkr.co/edit/r9hMZk?p=preview
I have a ui-grid where I have enabled multi selection. I want to be able to update the data whilst preserving the selection. If I just update the data then the selection is not preserved.
$scope.gridOpts.data = data2;
However I have defined a rowIdentity function, so that the id column uniquely identifies a row.
"rowIdentity" : function(row) {
return row.id;
}
Now if I select rows with id=Bob and id=Lorraine, then update the data, the rows are still selected. However the other fields in those rows are not updated.
How can I both preserve the selection and update all the data?

I think you need to keep track of you IDs yourself. So, you should remove the rowIdentifier, and instead add this piece at the beginning of your swapData function.
$scope.selIds = [];
for (var selRow of $scope.gridApi.selection.getSelectedRows()) {
$scope.selIds.push(selRow.id);
}
In addition to that, add an event handler on rowsRendered to re-select the previously selected rows
gridApi.core.on.rowsRendered($scope,function() {
for (var selId of $scope.selIds) {
for (var row of $scope.gridOpts.data) {
if (selId == row.id) {
$scope.gridApi.selection.selectRow(row);
}
}
}
});
You can put this in your registerApi callback.

Related

Adding a custom "ALL" (Total) row to the top of an ag-grid that is selectable like any other row

I have an ag-grid table with 2 columns - one text and one number.
I need to add a custom row to the top of my ag-grid table with the total value of the number column and the word "ALL" in the text column.
This needs to be on the top. And should not change its position even if the table is sorted.
Also, the row should be selectable.
Any help in this regard will be highly appreciated!
Sounds like you are describing Row Pinning which is already a feature in AG Grid. However, since you've also stated that row selection is a requirement, this will not be possible with Row Pinning as it's not supported.
Instead what I'd recommend is adding an extra row object inside the rowData for the custom row, and handling the update of the number column and the custom row position yourself when necessary:
If you want to handle the total value of the number column then you can use the following logic:
function updateTotalRowNode() {
let totalRowNode;
let sum = 0;
gridOptions.api.forEachNode((node) => {
if (node.data.totalRow) {
totalRowNode = node;
} else {
sum += node.data.gold;
}
});
totalRowNode.setDataValue('gold', sum);
}
If you want to keep the custom row always on the top row then you can implement the postSort callback:
postSort: (rowNodes) => {
// here we put All row on top while preserving the sort order
let nextInsertPos = 0;
for (let i = 0; i < rowNodes.length; i++) {
const athlete = rowNodes[i].data.athlete;
if (athlete === 'All') {
rowNodes.splice(nextInsertPos, 0, rowNodes.splice(i, 1)[0]);
nextInsertPos++;
}
}
},
See the following plunker for the implementation.

Programmatically change grid column order

I want to sort the columns in my grid, just like the rows. I have made a simple sort function that is called from an actioncolumn handler:
sortColumns:function(record) { // The record after which's values the columns are ordered
var columns = this.columns;
Ext.Array.sort(columns,function(col1,col2) {
if(record.get(col1.dataIndex) > record.get(col2.dataIndex)) return 1;
if(record.get(col1.dataIndex) < record.get(col2.dataIndex)) return -1;
if(col1.dataIndex > col2.dataIndex) return 1;
if(col1.dataIndex < col2.dataIndex) return 1;
throw new Error("Comparing column with itself shouldn't happen.");
});
this.setColumns(columns);
});
The setColumns line now throws the error
Cannot add destroyed item 'gridcolumn-1595' to Container 'headercontainer-1598'
which is because the "old" columns are destroyed first, and then the "new" columns, which are the same and thus destroyed, are applied.
I only want to change the order, but I didn't find any function to do it. Do you know how to do it?
Drag-drop ordering of the columns works, so it is doable; but I don't find the source code where sencha did implement that drag-drop thingy. Do you know where to look for that code?
Reconfigure method needs two arguments
grid.reconfigure(store, columns)
Here is the fiddle that changes the columns programatically https://fiddle.sencha.com/#fiddle/17bk
I have found that columns are items of the grid's headerCt, so the following works well, and unlike the other answers, it does not create new column components, keeping the column state and everything:
var headerCt = normalGrid.headerCt,
columns = headerCt.items.getRange();
Ext.Array.sort(columns,function(col1,col2) {
if(record.get(col1.dataIndex) < record.get(col2.dataIndex)) return -1;
if(record.get(col1.dataIndex) > record.get(col2.dataIndex)) return 1;
if(col1.dataIndex < col2.dataIndex) return -1;
if(col1.dataIndex > col2.dataIndex) return 1;
return 0;
});
headerCt.suspendLayouts();
for(var i=0;i<columns.length;i++)
{
headerCt.moveAfter(columns[i],(columns[i-1] || null));
}
headerCt.resumeLayouts(true);
There is a reconfigure method which can be used to achieve reordering, e.g:
grid.reconfigure(columns);
Check the this.
I couldn't manage to do it without storing columns in a custom field and using reconfigure, maybe someone can suggest something better (reconfigure doesn't work well with just regular columns field it seems):
Ext.define('MyGrid', {
extend: 'Ext.grid.Panel',
//just renamed "columns"
myColumnConfigs: [
//all your column configs
]
});
//to rearrange inside controller, also need to call it on grid render
var grid = this.getView();
var columns = grid.myColumnConfigs;
//...do your sorting on columns array
grid.reconfigure(columns);

Resetting single row values

I am using smart-table to display tabular data. One column of the table is editable. It is displayed and edited in an input field. Each row has a reset button which is supposed to reset edits made in the input field to the initial state.
How do I reset values of a single row (input field) when using smart-table?
Specifically what needs to be in the following function:
$scope.resetItem = function(index) {
// .....
};
I don't know about smart-table but why don't you try to get a copy of the data upfront and replace the related row with the original one?
var initialData = angular.copy(data);
$scope.data = data;
$scope.resetItem = function(index) {
$scope.data[index] = angular.copy(initialData[index]);
}

How to count number of rows in sencha gridview?

I have a Gridview on my page and I'm using buffered store. Is there a way to get the visible number of row count. Thank you
Here is a sample code that you can try: (I hope you'll get some idea from this)
// The below condition has to be checked for each record
// record: record instance
var me = this; // grid scope
Ext.Array.each(me.columns, function (item) { // iterate through each column in the grid
if (item.hidden || !item.dataIndex) { // you can avoid hidden columns and one's that re not bound to the store
return;
}
var cellVal;
try {
cellVal = Ext.fly( me.view.getCell(record, item)).select('cell selector class').elements[0].innerHTML;
} catch (e) {
// handle if you want
}
if (!Ext.isEmpty(cellVal)) {
// this record has been rendered
}
}, this);
This will get you all the records that are rendered. Since you are using a bufferedRenderer, this will also return the records that are rendered but not in the view, you can check and put an offset for the buffer.
Note: I've a similar logic in working in ExtJs 5 but haven't tested in touch.

Temporary ng-model?

I have a table that displays SQL data. I'm trying to only allow the user to edit an entry within a row if that value already exists somewhere in that column.
For example, if I tried to change the value "Strawberry" in the column "Fruit" to "Grape", when I click out of the text field, it should only keep the value "Grape", if it already exists within the column "Fruit". Otherwise, it should return to "Strawberry".
I am trying to accomplish this like so:
On ng-focus, I call recordValue(). This gets the entire row, the column, and the value, and sets it in a separate variable.
On ng-blur, I call validateValue(), and check all of my rows for
a) the original row index
b) to see if the value exists already in any row in the table
c) if it does, keep it, else revert to original value
(use the original row variable to replace edited row at original row index)
However, the row I pass is an ng-model of row[column] (value of a column in a row). This means that I can't have the original value of the row, because it will update the original data as I change it.
To solve this, I created a $watch on the data. If the value for $scope.validated was true, update the variable the table uses as a data source. Otherwise, don't. $scope.validated is set to false when recordValue() is called, and true after looping through validateValue() and finding the value, or resetting it.
The problem is, the data still updates so I can't revert the value because the original row doesn't exist; when I loop through the data source, only the updated row exists. I can't find the original row to revert to.
I'm not sure why this is happening. Am I doing something incorrectly?
HTML:
<table>
<tr>
<th ng-repeat="columnName in columnArray">{{columnName}}</th>
</tr>
<tr ng-repeat="row in filteredData">
<td ng-repeat="columnName in columnArray">
<input ng-model="row[columnName]" ng-focus="recordValue(row,row[column])" ng-blur="validateValue(row,column,row[column])">
</td>
</tr>
</table>
Angular:
$scope.recordValue = function (row, val) {
$scope.originalRow = row;
$scope.originalValue = val;
$scope.validated = false;
};
$scope.validateValue = function (row, column, val) {
// row is the row of data the text field is in
// column is column name at row
// val is value at column in row(ng-model on $scope.data)
var indexOfOriginalRow;
for (var i = 0; i < $scope.data.length; i++) {
var currentRow = $scope.data[i];
if (currentRow == $scope.originalRow) {
console.log("found it!");
indexOfOriginalRow = i;
}
else if (currentRow[column] == val) {
$scope.validated = true;
// the value was found, keep it as is
return;
}
}
$scope.validated = true;
$scope.data[index][column] = $scope.originalValue;
};
$scope.$watch("data", function () {
if ($scope.validated) {
$scope.filteredData = doStuffWithData($scope.data);
}
}, true);
Each row is an object within an array (the datasource). When the values change, I update the PARENT data source, but not the data source the table uses. Unfortunately, it seems as though the table's data source (filteredData) updates anyways.
This prevents me from resetting the value of the text field because the value is updated in the data source, so it seems like it already exists in the table. It appears to be a logical error, but it is difficult to conceptualize the table and variable data structure, so I can't find the error.
UPDATE:
Even when I only store the value, and not reference of the original row, the bigger problem is that the data still updates. The original row is useless because the new row is already in the data source, meaning it will be found, viewed as a pre-existing value, and allow anything you type. Very frustrating.
Try using angular.copy() to store the value of row in $scope.originalRow without binding them.
I think you will always set $scope.validated = true and return because you are checking the updated row with the updated value. Try adding an else:
for (var i = 0; i < $scope.data.length; i++) {
var currentRow = $scope.data[i];
if (currentRow == $scope.originalRow) {
console.log("found it!");
indexOfOriginalRow = i;
} else if (currentRow[column] == val) { // ADDED ELSE
$scope.validated = true;
// the value was found, keep it as is
return;
}
}

Resources