How to count number of rows in sencha gridview? - extjs

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.

Related

Using AngularJS to validate dynamically created 'input' element

I have a table that displays several entries, each has an <input>. The user can dynamically add additional inputs by clicking an "add entry" button. I need to iterate over them before saving and validate each one. I simplified my example to check that the value of each input is greater than 100 (ultimately I will use a pattern-match to validate MAC and IP addresses).
I can probably handle it if I could select all <input>s, but I would really like to select a specific <input> using an index I already have in my scope. I read that angular.element is a way, but I need to select something that was dynamically created, and thus not named something easy like id="myInput". Unless I use an id of "input" and append a unique number with Angular's $index in the id attribute?
Here is my Fiddle that shows what I'm doing. Line 44 is an if() that should check if any <input> is greater than 100. The "Save Row" button validates that the input is greater than 100, but if you edit a line, I need the "Save" button to validate any that the user has edited (by clicking Edit next to it).
tl;dr:
How can I use Angular to select an <input> that has been created dynamically?
I have updated your fiddle in a clean way so that you can maintain the validation in a generic method for both add & edit.
function validateBinding(binding) {
// Have your pattern-match validation here to validate MAC and IP addresses
return binding.ip > 100;
}
Updated fiddle:
https://jsfiddle.net/balasuar/by0tg92m/27/
Also, I have fixed the current issue with editing you have to allow multiple editing without save the first row when clicking the next edit on next row.
The validation of 'save everything' is now cleaner in angular way as below.
$scope.changeEdit = function(binding) {
binding.onEdit = true;
//$scope.editNum = newNum;
$scope.showSave = true;
};
$scope.saveEverything = function() {
var error = false;
angular.forEach($scope.macbindings, function(binding) {
if(binding.onEdit) {
if (validateBinding(binding)) {
binding.onEdit = false;
} else {
error = true;
}
}
});
if (error) {
alert("One/some of the value you are editing need to be greater than 100");
} else {
$scope.showSave = false;
}
}
You can check the updated fiddle for the same,
https://jsfiddle.net/balasuar/by0tg92m/27/
Note: As you are using angular, you can validate the model as above and no need to retrieve and loop the input elements for the validation. Also for your case, validating the model is sufficient.
If you need some advanced validation, you should create a custom
directive. Since, playing around with the elements inside the
controller is not recommended in AngularJS.
You can use a custom class for those inputs you want to validate. Then you can select all those inputs with that class and validate them. See this Fiddle https://jsfiddle.net/lealceldeiro/L38f686s/5/
$scope.saveEverything = function() {
var inputs = document.getElementsByClassName('inputCtrl'); //inputCtrl is the class you use to select those input s you want to validate
$scope.totalInputs = inputs.length;
$scope.invalidCount = 0;
for (var i = 0; i < inputs.length; i++){
if(inputs[i].value.length < 100){
$scope.invalidCount++;
}
}
//do your stuff here
}
On line 46 a get all the inputs with class "classCtrl" and then I go through the input s array in order to check their length.
There you can check if any of them is actually invalid (by length or any other restriction)

Preserve selection in angular ui-grid while updating data

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.

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);

ui-grid infinite scroll with row processor filtering

I have an angularjs app using ui.grid with the infinite scrolling module. I am using whole row filtering as described in the documentation like so:
function MyController($scope){
var that = this;
var getData = function(){
//logic left out for brevity
};
var onRegisterApi = function(gridApi){
gridApi.grid.registerRowsProcessor(function (rows) {
return that.filterRowProcessor.apply(that, [rows]);
}, 200);
gridApi.infiniteScroll.on.needLoadMoreData($scope, getData);
};
this.options["onRegisterApi"] = onRegisterApi;
}
//...code excluded for brevity...
MyController.prototype.filterRowProcessor = function(renderableRows){
renderableRows.forEach(function(row) {
if (this.selectedMap[row.entity["Id"]]) {
row.visible = false;
}
});
return renderableRows;
}
The idea is to filter out rows which have an Id belonging to a specific collection; which works as designed. My problem is that when I get my first page of data the filter row processor removes enough rows from visibility that my scroll bar disappears. This in turn causes the infinite scroll api to never raise the "needLoadMoreData" event.
Is this uncharted territory, or is there a way around this? I am also open to not filtering by that mechanism if its easier to do another way.
UPDATE (01/08/2016)
I have found a work around that I don't like very much. Essentially I have a known page size and if the data coming in to the grid is less than that page size and my callback returns false for "end of query", I automatically issue a next page query. I would rather find a solution via the grid api, but for now this will work.
if(this.itemsSource.data.length < constants.PAGE_SIZE && !continuation.endOfQuery){
//call get data again
}
After thinking about it for a while I decided on the below method as my solution. I am still open to suggestions if it makes more sense to do it a different way. Rather than relying on a length of data (which only loosely translates to having a scroll bar) I decided to calculate the height of the total rows visible, compared to the viewport of the grid.
//this method get called from the callback from needsMoreData
//hasMoreData is the same boolean flag sent in to dataLoaded
var shouldRetrieveMore = function (gridApi, hasMoreData){
if (!hasMoreData) {
return false;
}
var totalCountOfRows = gridApi.grid.getVisibleRowCount();
if (totalCountOfRows === 0) {
return true;
}
var height = gridApi.grid.getViewportHeight();
var heightOfRow = gridApi.grid.getVisibleRows()[0].$$height;
return ((heightOfRow * totalCountOfRows) <= height);
}
One additional addendum to the solution could be to sum the $$heights of all the rows, but I decided against it since in my uses they are always the same height.

eXTjS 3.3.1 LiveGrid store.each gives only buffered rows

I use ExtJs 3.3.1 because many extensions don't work under 4.xx
One of these extensions is LiveGrid.
I can't try but i suppose a simmilar thing happens with a 4.x buffered grid.
When i do a report of the lines visible in the grid only the buffered lines are returned, i reposition the current record but the loading of the rest of the records only happens after the reporting finishes. How can i get all the records ?
In an button handler i call toReport(grid).
toReport = function(grid){
var store = grid.getStore();
var view = grid.getView();
store.each(function(record) {
Ext.defer(function(){
index = readRow(store, record);
if (index % 10 == 0){
view.focusRow(index);
}
}, 500, this);
});
console.log(output)
}
readRow = function(store, record){
output = "";
for (var xlCol=1;xlCol<record.fields.length+1;xlCol++){
var veld = store.fields.itemAt(xlCol-1).name;
waarde = record.get(veld);
if (realTypeOf(waarde)==="date"){
output += waarde.format("d-m-Y");
}else{
output += record.get(veld);
}
}
console.log(store.indexOf(record)+ " " + output);
return store.indexOf(record);
}
The grid needs to manipulate its store filters, sorters, paging, etc., in order to obtain the records it want to display. The store itself only keeps in memory the subset of records that matches its filters, etc. That is the way stores are designed in Ext: they are intended to be bounded to one and only one view.
I think in your case, the simplest solution is to create another store with a similar configuration, and use its load method with params such that you get all the records.
If you're reticent to fire multiple requests for retrieving essentially the same data, have a look at Ext.data.Proxy. Unlike stores, proxies are not bound to a specific view or task and can be shared between multiple store instances. So in theory, you can create a proxy that requests all the records from the server at once, and then feeds a subset of them to multiple stores. For that you'll have to implement the doRequest method of the proxy (or most probably overrides the one of the proxy you're already using).
I did find a solution by using recursion.
Like this the view kan keep up with the enumeration.
Ext.toExcel = function(grid){
var store = grid.getStore();
var view = grid.getView();
readRow(store, view, 0);
}
readRow = function(store, view, index){
output = "";
record = store.getAt(index);
for (var xlCol=1;xlCol<record.fields.length+1;xlCol++){
var veld = store.fields.itemAt(xlCol-1).name;
waarde = record.get(veld);
if (realTypeOf(waarde)==="date"){
output += waarde.format("d-m-Y");
}else{
output += record.get(veld);
}
}
//console.log(index+ " " + output);
if (index % 50 == 0){view.focusRow(index);}
Ext.defer(function(){
if (index < store.totalLength){
readRow(store, view, index+1);
}
}, 100, this);
}

Resources