Related
I am using the Angular Datatables library in my project, I am fetching the data from an URL which returns JSON object, I put the data in an array and use it to populate my table.
appenditems(){
this.items = [];
this.items2 = [];
this.items3 = [];
$.getJSON(this.urlbuild[0].toString(), ( data:any ) => {
var p, pr;
var test = data.features;
for (p in test){
var row = []
for (pr in test[p].properties){
row.push(test[p].properties[pr])
}
this.items.push(row)
}
})
}
I initialize the dtOptions as
this.dtOptions = {
data: this.items,
columns:[
{title: "Column1"},
{title: "Column2"},
{title: "Column3"}
],
paging: false,
scrollY: 180,
scrollX: true,
processing: true,
dom: 'Brtip',
buttons: [
'copy', 'csv', 'excel', 'print'
],
responsive: true
};
My html looks like this
<table id="table1" datatable [dtOptions]="dtOptions" class="display" style="width:70%"></table>
The issue I am facing is with re-rendering or refreshing the table. I apply and append CQL filter to the URL and then it returns only the required data as JSON. I want the table to be refreshed after I append the URL with those parameters. I have tried ajax.reload(), and re-rendering using this which is basically destroy and re-render using dtTrigger but it doesn't seem to work.
If you need more information about the code or how something is working please comment and ask.
you can use reload() function: rerender your table
import { DataTableDirective } from 'angular-datatables';
dtElement: DataTableDirective;
dtInstance: Promise<DataTables.Api>;
rerender(): void {
this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
dtInstance.ajax.reload()
});
I'm working on an application which is supported by an OData REST API. The API works fine in tools like Postman but when we try and use it with our Web App and pull the returned data (which is Json) into a Grid Panel there is an issue.
My company wants to be able to support large data sets, so we choose Ext JS based on it's BufferedStore Grid.
So the problem is we can't get it to work. The way the API is set up is based on the current OData 4 Spec. So it uses $skip and $top exactly in the same way you would use the $startParam and $limitParam within the Proxy of my Ext Store.
So for instance when I want to fetch data from my API the URL would look something like this http://127.0.0.1/odata/BEER/?$skip=0&$top=100 to fetch the first 100 rows of data,
And http://127.0.0.1/odata/BEER/?$skip=1&$top=100 to fetch the for the next 100 rows. The data is in Json and returns back with now issues outside of the BufferedStore Grid.
The Json looks like so
{"#odata.context":"http://127.0.0.1:8080/odata/$metadata#BEER_SAMPLE_LOT","value":[{"Id":17452707,"Name":"BS860-2","Barcode":"BS860-2","Sequence":2578,"Created":null,"Modified":null,"Active":true,"LikedBy":0,"FollowedBy":0,"CI_LOT_NUM":2,"CI_INTENDED_USAGE":"Turbidity Testing","CI_TRIGGER_RUNS":0},{"Id":17452798,"Name":"BS986-2","Barcode":"BS986-2","Sequence":2596,"Created":null,"Modified":null,"Active":true,"LikedBy":0,"FollowedBy":0,"CI_LOT_NUM":2,"CI_INTENDED_USAGE":"Turbidity Testing","CI_TRIGGER_RUNS":0}]}
The problem simple is that when the page loads the Grid will load the first 100 rows, but then as you scroll down the it doesn't prefetch (as the documentation and demos promise it should). It just stops at 100. Nothing we have tried will make it try and fetch the next set. How is this supposed to work?
Here is my code for the store which supports the grid.
var oDataModel = Ext.create('Ext.data.Model',{
fields: []
});
var oDataStore = Ext.create('Ext.data.BufferedStore', {
storeId: 'beerStore',
buffered: true,
autoLoad: true,
model: oDataModel,
leadingBufferZone: 100,
pageSize: 100,
proxy:{
type: 'rest',
url: http:127.0.0.1/odata/BEER
pageParam: false, //to remove param "page"
startParam: '$skip',
limitParam: '$top',
noCache: false, //to remove param "_dc"
method: 'GET',
withCredentials: true,
headers : {
"Content-Type": "text/json",
},
reader:{
type:'json',
rootProperty: 'value',
},
listeners: {
exception: function(proxy, response, operation, eOpts) {
console.log("Exception");
console.log(response);
}
}
}
});
Below is my Grid which is dynamic.
// Create Basic Ext Grid
var oDataGrid = Ext.create('Ext.grid.Panel', {
title: 'oData Entity Table',
store: oDataStore,
height: 400,
loadMask: true,
selModel: {
pruneRemoved: false
},
plugins: [{
ptype: 'bufferedrenderer',
trailingBufferZone: 10,
leadingBufferZone: 10,
scrollToLoadBuffer: 10
}],
renderTo: 'oData-grid'
});
// Add columns to the data grid
_that.buildColumns(_that._properties.property, oDataGrid);
buildColumns: function(__cols,_bGrid){
var _that = this;
// Add the row number first
_bGrid.headerCt.insert(_bGrid.columns.length - 1,{ xtype: 'rownumberer', width: 50 });
var _column = '';
for (var n=0; n<__cols.length; n++) {
//console.log(__cols[n].name);
_column = Ext.create('Ext.grid.column.Column', {
text: __cols[n].name,
width: 100,
dataIndex: __cols[n].name,
filter: true,
renderer: function(__value){
var _val = '';
if(__value instanceof Array){
for(var i = 0; i < __value.length; i++){
_val += __value[i].Barcode + "<br>";
}
} else if (__value instanceof Object){
if(!(__value instanceof Date)){
_val = __value.Barcode + "<br>";
}else{
_val = __value;
}
} else {
_val = __value;
}
return _val;
}
});
_bGrid.headerCt.insert(_bGrid.columns.length - 1, _column); //inserting the dynamic column into grid
}
}
My problem is that I Have Hierarchical grid (Master and Child) let say I Have a Department Grid it contains List of Employee Grid, and they both use same datasource.
Here's my GridChild Code:
function detailInit (e){
var msterRow = e.sender.items().index(e.masterRow).toString();
var grid = $("<div id='childGrid"+msterRow+"'
class=childGrid'/>").appendTo(e.detailCell).kendoGrid({
data: e.data.DeptEmployees,
schema: {
model: { fields: { foo: {--skip--}, bar: {--skip--} } }
},
toolbar: ["create", "cancel", "save"],
editable: "popup",
columns: [ --skip--]
save: function(e){
ajaxUpdateDepartment(msterRow, this.dataSource.data());
}
})
As you can see i use data: e.data.DeptEmployees, as child data source to fetch data.
Now I'm stacked in how can I update the child data source?
What I have Tried:
I add child's dataSource.transport for updates, but my child grid keeps on loading.
So I end up configuring the save: function (e) and simply send all data source of the current child but popup editor didn't close at all. And I'm having difficulty to refresh the child data source.
I also attempt to convert my Master and Child Grid to ASP Razor but there was no definite example if how could I handle it in back end, and also my child grid contains drop down grid, so that would be a big re-do. And I also don't know if how can I pass customize parameter through it
I am desperate, I can't find any working reference except this one. but it's using odata, and I dont have child id to use as reference, since I am only using list which I retrieve in a user event.
Please help :'( I'm taking too much time for this one.
The solution is to define a transport properties, in order to fetch data from master, I only need to define the data and convert that to Jason.
take a look of these code:
function detailInit (e){
var msterRow = e.sender.items().index(e.masterRow).toString();
var grid = $("<div id='childGrid"+msterRow+"'
class=childGrid'/>").appendTo(e.detailCell).kendoGrid({
//data: e.data.ChildDetails,
transport: {
read: function (o) {
console.log("child read");
var data = e.data.ChildDetails.toJSON();
o.success(data);
},
update: function (o) {
console.log("child update");
var data = o.data,
arentItem = findByID(data.id);
for (var field in data) {
if(!(field.indexOf("_") === 0)){
arentItem[field] = data[field];
}
}
e.data.dirty = true;
saveChild(record, "#suffix", msterRow, "update");
o.success();
},
destroy: function (o) {
var parentItem = findByID(o.data.id);
preventBinding = true;
e.data.ChildDetails.results.remove(parentItem);
o.success();
saveChild(record, "#suffix", msterRow, "destroy");
},
create: function (o) {
console.log("child create");
var record = o.data;
record.id = index;
index++;
saveChild(record, "#suffix", msterRow, "create");
o.success(record);
}
},
schema: {
model: { fields: { foo: {--skip--}, bar: {--skip--} } }
},
toolbar: ["create", "cancel", "save"],
editable: "popup",
columns: [ --skip--]
}
Here's the working dojo snippet
I have a service that will return my some config options for an ng-grid. The getGridOptions function takes the name of the controller it's used on and returns the correct set of options (only one shown here for brevity).
service for ng-grid options:
angular.module('services').service('GridOptionsService',function(){
var documents = {
data: 'myData',
enablePaging: true,
showFooter:true,
totalServerItems: 'totalServerItems',
pagingOptions: {
pageSizes: [50,100,200],
pageSize: 50,
currentPage: 1
},
filterOptions: {
filterText: '',
useExternalFilter: false
},
enableCellEdit: false,
enableColumnReordering: true,
enablePinning: false,
showGroupPanel: false,
groupsCollapsedByDefault: true,
enableColumnResize: true,
showSelectionCheckbox: true,
selectWithCheckboxOnly: true,
columnDefs: [
{field:'docId', displayName:'Document ID', cellTemplate: NgGridDomUtil.toLink('#/documents/{{row.getProperty(col.field)}}')},
{field:'docTags', displayName:'Tags'},
{field:'lastSaveDate', displayName:'Last saved'},
{field:'documentVersion', displayName:'Version', width: 120},
{field:'busDocId', displayName:'Customer Doc ID'},
{field:'markedForDelete', displayName:'Deleted', width: 120, cellTemplate: NgGridDomUtil.toCheckbox('{{row.getProperty(col.field)}}')}]
};
var gridOptionManager = {
documents: documents
}
return {
getGridOptions: function(controllerName){
return gridOptionManager[controllerName];
}
}
})
The NgGridDomUtil class just makes it easier to style things on the grid:
var NgGridDomUtil = (function(){
var toLink = function(href){
var html = '<div class="ngCellText" ng-class="col.colIndex()"><a ng-href= "'+href+'" class="ngCellLink"><span ng-cell-text>{{row.getProperty(col.field)}}</span></a></div>'
return html;
}
var toCheckbox = function(_selected){
var html = '<div class="ngCellText" ng-class="col.colIndex()"><input type="checkbox" ng-change="console.log('+"TEST"+')" ng-model="COL_FIELD" ng-input="COL_FIELD"' + (_selected ? 'selected' : '') + ' /></div>'
return html
}
return {
toLink: toLink,
toCheckbox: toCheckbox
}
})();
My problem is what when I use the GridOptionsService to retrieve the data, the data is still presented to the grid correctly, but the text filtering no longer works and the paging is broken. However, the selectedFilterOption still works.
controller:
angular.module('controllers').controller('Repository', ['$scope', 'DataContext','GridOptionsService','$http', function($scope, DataContext,GridOptionsService,$http) {
$scope.filterOptions = {
filterText: '',
useExternalFilter: false
};
$scope.totalServerItems =0;
$scope.pagingOptions ={
pageSizes: [5,10,100],
pageSize: 5,
currentPage: 1
}
//filter!
$scope.dropdownOptions = [{
name: 'Show all'
},{
name: 'Show active'
},{
name: 'Show trash'
}];
//default choice for filtering is 'show active'
$scope.selectedFilterOption = $scope.dropdownOptions[1];
//three stage bool filter
$scope.customFilter = function(data){
var tempData = [];
angular.forEach(data,function(item){
if($scope.selectedFilterOption.name === 'Show all'){
tempData.push(item);
}
else if($scope.selectedFilterOption.name ==='Show active' && !item.markedForDelete){
tempData.push(item);
}
else if($scope.selectedFilterOption.name ==='Show trash' && item.markedForDelete){
tempData.push(item);
}
});
return tempData;
}
//grabbing data
$scope.getPagedDataAsync = function(pageSize, page, searchText){
var data;
if(searchText){
var ft = searchText.toLowerCase();
DataContext.getDocuments().success(function(largeLoad){
//filter the data when searching
data = $scope.customFilter(largeLoad).filter(function(item){
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
})
$scope.setPagingData($scope.customFilter(data),page,pageSize);
})
}
else{
DataContext.getDocuments().success(function(largeLoad){
var testLargeLoad = $scope.customFilter(largeLoad);
//filter the data on initial page load when no search text has been entered
$scope.setPagingData(testLargeLoad,page,pageSize);
})
}
};
//paging
$scope.setPagingData = function(data, page, pageSize){
var pagedData = data.slice((page -1) * pageSize, page * pageSize);
//filter the data for paging
$scope.myData = $scope.customFilter(pagedData);
$scope.myData = pagedData;
$scope.totalServerItems = data.length;
// if(!$scope.$$phase){
// $scope.$apply();
// }
}
//watch for filter option change, set the data property of gridOptions to the newly filtered data
$scope.$watch('selectedFilterOption',function(){
var data = $scope.customFilter($scope.myData);
$scope.myData = data;
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
$scope.setPagingData($scope.myData,$scope.pagingOptions.currentPage,$scope.pagingOptions.pageSize);
})
$scope.$watch('pagingOptions',function(newVal, oldVal){
$scope.getPagedDataAsync($scope.pagingOptions.pageSize,$scope.pagingOptions.currentPage,$scope.filterOptions.filterText);
$scope.setPagingData($scope.myData,$scope.pagingOptions.currentPage,$scope.pagingOptions.pageSize);
},true)
$scope.message ="This is a message";
$scope.gridOptions = {
data: 'myData',
enablePaging: true,
showFooter:true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions,
enableCellEdit: true,
enableColumnReordering: true,
enablePinning: true,
showGroupPanel: true,
groupsCollapsedByDefault: true,
enableColumnResize: true
}
$scope.gridOptions = GridOptionsService.getGridOptions('documents');
//get the data on page load
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
}]);
The grid options that I have hard coded into the controller are the same as the ones returned from the service. What I don't understand is why the grid renders, the dropdown filter works, but the paging is broken, only when the options for the grid come from a service? But it works as expected if it's hard coded into the controller.
EDIT: If someone can more eloquently state my problem, feel free to edit the title.
I don't really know how the ngGrid is implemented, but a common pitfall I do know that exist in many directives, is they expect their configurations to be ready as soon as they're initialized. Meaning that instead of watching the configuration object, they assume it exists and use it directly in the link\controller functions which runs as soon as they're created.
If this is indeed the case, a quick workaround to the problem is initializing the directive only when you have the configuration object. Let's say you pass on the configuration object through the variable 'options' on your scope, you'll then write something like:
<!-- If options exists on your scope, it means you fetched it from the server -->
<div ng-if="options">
<div ng-grid ng-grid-options="options"></div>
</div>
Again, I'm not familiar with ngGrid or its usage, this is just an educated guess, take the conclusions and apply them on the correct API.
I haven't tested this, but one possible issue is that you are overwriting an object that is on the $scope. This can break the two way binding. For a quick test try
$scope.grid = {
Options: {
data: 'myData',
enablePaging: true,
showFooter:true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions,
enableCellEdit: true,
enableColumnReordering: true,
enablePinning: true,
showGroupPanel: true,
groupsCollapsedByDefault: true,
enableColumnResize: true
}
}
$scope.grid.Options = GridOptionsService.getGridOptions('documents');
You would need to update the grid options in the directives attribute as well of course.
The problem is that the controller functions for filtering and paging use the options defined on the controller $scope, but the ng-grid UI is not bound to those objects.
The controller methods for filtering and paging use $scope.pagingOptions as the data source. However, the ng-grid UI is bound to $scope.gridOptions.pagingOptions. When you create the $scope.gridOptions explicitly in the controller, $scope.gridOptions.pagingOptions refers to the same object as $scope.gridOptions, so making changes in the UI will change the value used in the controller functions.
However, when $scope.gridOptions is assigned using the service, the service is creating new pagingOptions, so there is no connection between $scope.gridOptions.pagingOptions and $scope.pagingOptions. Making changes in the UI will not change the values used in the controller functions.
The same is true for filterOptions.
One way to resolve the issue is
$scope.gridOptions = GridOptionsService.getGridOptions('documents');
$scope.pagingOptions = $scope.gridOptions.pagingOptions
$scope.filterOptions = $scope.gridOptions.filterOptions
I've created my first ng-grid table and loading it with data asynchronously from the server. All my code seems to get executed created and function as desired behind the scenes except for one major problem.
ng-grid's pagination buttons get rendered in HTML as 'button' tags like this:
<button class="ngPagerButton" ng-click="pageForward()" ng-disabled="cantPageForward()" title="Next Page">
<div class="ngPagerLastTriangle ngPagerNextTriangle"></div>
</button>
But there is no type attribute put on the button so it defaults to type='submit'.
Whenever I click on the next page button, behind the scenes everything works, but it also does an extraneous POST because of the button type. This is completely undesired.
Anyone else run into this? How do you get around it? Am I doing something wrong?
The basic setup is as follows:
HTML:
<div id='deposits' class='gridStyle' ng-grid='gridOptions'></div>
JS controller code (really nothing special here, taken pretty much right from the ng-grid docs...but not working!):
$scope.reportingForm = {
startDate: new Date(2014, 1, 1), // just for testing
endDate: new Date(2014, 1, 7),
};
$scope.filterOptions = {
filterText: '',
useExternalFilter: true
};
$scope.totalServerItems = 0;
$scope.pagingOptions = {
pageSizes: [7, 14],
pageSize: 7,
currentPage: 1
};
$scope.setPagingData = function(data, page, pageSize) {
var pagedData = data.slice((page - 1) * pageSize, page * pageSize);
$scope.data = pagedData;
$scope.totalServerItems = data.length;
if (!$scope.$$phase) {
$scope.$apply();
}
};
$scope.getPagedDataAsync = function(pageSize, page, searchText) {
setTimeout(function () {
var data;
if (searchText) {
var filter = searchText.toLowerCase();
myService.getDataAsync(
$scope.reportingForm.startDate, $scope.reportingForm.endDate
function(result) {
data = result.result.data.filter(function(item) {
return JSON.stringify(item).toLowerCase().indexOf(filter) != -1;
});
$scope.setPagingData(data, page, pageSize);
}
);
} else {
myService.getDataAsync(
$scope.reportingForm.startDate, $scope.reportingForm.endDate,
function(result) {
$scope.setPagingData(result.result.data, page, pageSize);
}
);
}
}, 100);
};
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
$scope.$watch('pagingOptions', function (newVal, oldVal) {
if (newVal !== oldVal && newVal.currentPage !== oldVal.currentPage) {
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage, $scope.filterOptions.filterText);
}
}, true);
$scope.$watch('filterOptions', function (newVal, oldVal) {
if (newVal !== oldVal) {
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage, $scope.filterOptions.filterText);
}
}, true);
$scope.gridOptions = {
data: 'data',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions,
enablePaging: true,
showFooter: true,
columnDefs: [
{ field: 'date', displayName: 'Date' },
{ field: 'id', displayName: 'Id' },
{ field: 'location', displayName: 'Location' },
{ field: 'amount', displayName: 'Amount' },
]
};
ng-grid renders the previous and next page buttons as <button> and does not specify a button type so it defaults to type='submit'.
In our page the ng-grid table lived within a form. I didn't know this but after some testing realized submit <button>s within a form do a POST, but submit <button>s outside of a form do NOT do a POST.
By moving our ng-grid table outside of the form we no longer have this problem.
Really though, this seems to be an oversight in the ng-grid code. It should really be rendering the buttons as type='button' to prevent this kind of issue in the future. I can't imagine any cases where you'd want the last/next page buttons to do POSTs.
This appears to have been fixed in ng-grid 2.0.8 whenever it is released.
https://github.com/angular-ui/ng-grid/pull/693