Kendo UI Grid virtual scrolling makes infinite ajax hits - ajax

I am loading a Kendo UI grid with virtual scrolling option on and for un-identified reasons when I scroll down when inside the grid infinite hits are made to the get grid data method.
My page also has angular JS.
The page number is always sent as one, but it should at least be incremented to two.
Grid has been defined as following:
dataSource: {
serverPaging: true,
serverSorting: true,
serverFiltering: true,
pageSize: 10,
transport: {
read: URL
},
schema: {
data: function(data) {
/* Few business filter operation done here */
return data.Items;
},
total: function(data) {
if (data.Items != null && data.Items.length > 0) {
// Every record of the current data set has total employees count
// Thus returning the first value
return data.Items[0].totalEmployees;
}
return 0;
},
scrollable: {
virtual: true
},
sortable: true,
columns: columns,
dataBound: function(arg) {
/* few more business operations */
},
scrollable: {
virtual: true
},
serverPaging: true,
serverSorting: true,
serverFiltering: true,
editable: {
'mode': 'inline',
'createAt': 'top'
},
Image showing infinite hits:

I was loading the kendo.all.js file more than once, precisely thrice. I made sure it only loads once and the issue is gone.

Related

jqgrid in mvc with multiple delete

i want to add multiple delete functionality in my Jqgrid with MVC here there is code for my current grid in Action there is two thing View and delete but i want a check box and out side the grid one button when click on it the checked item should be delete and also before delete action fire message for confirmation please help.
$(document).ready(function ()
{
#if (ViewBag.Filters != string.Empty)
{
#Html.Raw(ViewBag.Filters);
}
$("#jq-grid").jqGrid({
url: '/Home/GetList',//Service URL to fetch data
datatype: 'json',//Data Type which service will return
mtype: 'GET',
postData://Data that will be posted with every request
{
filters: function () {
return $.toJSON([
//Here add the parameters which you like to pass to server
{ Key: "EmployeeName", Value: $("#txtEmployeeName").val() },
{ Key: "Id", Value: $("#txtId").val() }
]);
}
},
colNames: ['Employee Id', 'EmployeeName','empPhoto', 'Action'],//Column Names
colModel: [//Column details
{ name: "Employee Id", index: "Employee Id", width: "220px" },
{ name: "Employee Name", index: "EmployeeName", width: "220px" },
{ name: "empPhoto", index: "empPhoto", width: "50px" ,formatter:imageFormat },
//Do not allow sorting on Action Column
{ name: "Action", index: "Action", sortable: false, width: "220px" }
],
autowidth: true,//Do not use auto width
sortname: '#ViewBag.EmployeeGridRequest.sidx',//This will persist sorting column
sortorder: '#ViewBag.EmployeeGridRequest.sord', // This will persist sorting order
imgpath: '',//if some images are used then show grid's path
caption: 'Employee Grid List',//Grid's Caption, you can set it to blank if you do not want this
scrollOffset: 0,
rowNum: #ViewBag.EmployeeGridRequest.rows, //Total Pages
page: #ViewBag.EmployeeGridRequest.page, // Current Page
rowList: [#ViewBag.EmployeeGridRequest.rowList], // Page Selection Options
viewrecords: true,
// height: "100%",
altRows: true,//Use alternate row colors
altclass: 'jqgaltrow',//Alternate row class
hoverrows: true, //Do not hover rows on mouse over
pager: $('#jq-grid-pager'),//Div in which pager will be displayed
toppager: true,
pgbuttons: true,//Show next/previous buttons
loadtext:'Loading Data please wait ...',
//loadui :'block',//enable by default, block will disable inputs, remove this line if you do not want to block UI
loadComplete: function (response) //Incase you want to perform some action once data is loaded
{
}
});
});
This is one approach that I took after finding no built in solution that jqgrid api provides. Hope this helps. I took this approach because I wanted to allow deletion of those records that passed business rules and to alert user of those records where problem was found.
#jqGrid delete button
//Delete selected devices from jqgrid standard checkbox
jQuery("#jqDevice").jqGrid('navButtonAdd', '#jqDevicePager', {
caption: "",
title: "Delete selected records",
buttonicon: "ui-icon-trash",
position: "first",
onClickButton: function(){
//alert("edit button clicked");
var selIds = $("#jqDevice").getGridParam("selarrrow");
var len = selIds.length;
$("#dialogDeleteConfirm").dialog({
buttons : {
"Confirm" : function() {
//loop through each rows selected and delete
if(selIds.length > 0){
for(var i = len - 1; i >= 0; i--) { //traverse the selarrow array in reverse order.
//alert("Deleting Device... " + selIds[i]); //Test
$.ajax({
type: 'POST',
data: "deviceID=" + selIds[i],
url: '/Device/DeleteDeviceByID',
async: false,
success: function(data, textStatus){
//successfully deleted row
$("#jqDevice").delRowData(selIds[i]);
alert("Successfully deleted records...");
},
error: function (jqXHR, textStatus, errorThrown) {
alert(JSON.parse(jqXHR.responseText));
}
});
}
}else{
alert("No rows selected.");
}
//Close Dialog after user confirms delete action
$(this).dialog("close");
},
"Cancel" : function() {
$(this).dialog("close");
}
}
});
}
});
# Controller Action
public ActionResult DeleteDeviceByID(int deviceID)
{
try
{
//update LastModBy before deleting
Device device = _deviceRepository.GetDevice(deviceID);
device.LastModBy = User.Identity.Name;
_deviceRepository.SaveChanges();
_deviceRepository.DeleteDevice(deviceID);
}
catch (Exception ex)
{
//throw new Exception (ex.Message.Replace(Environment.NewLine, string.Empty));
//return Json(new { success = false, message = ex.Message.Replace(Environment.NewLine, string.Empty) });
//Good way to raise error in JSON format
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json("There was a problem deleting ID - " + deviceID + " : " + ex.Message.Replace(Environment.NewLine, string.Empty) +
"\r\n\r\n Please try again. If the problem continues, please contact ... for further assistance.");
}
return Json(new { success = true });
}

Fullcalendar eventOverlap issue

I use fullCalendar 2.6 and I only want one allday event per day.
I use this :
eventOverlap: false
It works if I put an event and move it on a day which has already an event : they won't overlap.
But if I click on a day which has already an event, it overlaps and I get 2 events (or more) at the same date...
I use this too on my select function :
overlap: false
which does not do the trick..
What can I do ? any idea ?
And another issue is when I use ajax to send start/end dates, it only works when I click to add an event but not when I move it using arrows when I put my cursor on the edge of the event to change its size...
My code :
function renderCalendar() {
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,basicWeek,basicDay',
},
defaultDate: dateNow,
defaultView: 'month',
lang: 'fr',
height: 'auto',
editable: true,
allDaySlot: true,
weekNumbers: false,
timeFormat: 'H:mm',
slotEventOverlap: false,
weekends: true,
selectable: true,
selectHelper: true,
eventOverlap: false,
select: function(start, end) {
var title = 'Occupé';
var eventData;
if (title) {
eventData = {
title: title,
start: start,
end: end,
allDay: true,
overlap: false,
color: '#bf0000',
textColor: '#ffffff',
};
$('#calendar').fullCalendar('renderEvent', eventData, true);
}
$('#calendar').fullCalendar('unselect');
alert(start);
alert(end);
}
});
Thanks for your help !
well.. selectOverlap set to "false" is the solution :)
for the second problem, the solution must be eventResizeStop but I didn't find it yet
Try this.
select: function(start, end) {
var title = 'Occupé';
var eventData;
var sameDayEvents = $('#calendar').fullCalendar( 'clientEvents' ,function(event) {
if (event.start.format('YYYY-mm-dd') == start.format('YYYY-mm-dd')) {
return true;
} else {
return false;
}
});
if (!sameDayEvents.length && title) {
eventData = {
title: title,
start: start,
end: end,
allDay: true,
overlap: false,
color: '#bf0000',
textColor: '#ffffff',
};
$('#calendar').fullCalendar('renderEvent', eventData, true);
}
$('#calendar').fullCalendar('unselect');
alert(start);
alert(end);
}

Summary + ajax paging on ExtJS.Grid

I have a grid with data similar to http://dev.sencha.com/deploy/ext-4.0.1/examples/grid/array-grid.html, but using paged proxy similar to http://dev.sencha.com/deploy/ext-4.0.1/examples/grid/paging.html.
I need to add a summary similar to http://dev.sencha.com/deploy/ext-4.0.1/examples/grid/group-summary-grid.html, only 1 at the bottom of the grid. It must sum all data, not only data from current page.
I can make the DB query, no need Ext to sum the values. But how will I send the summary data to Ext.store and from store to grid?
Handle this as example how you could implement.
This is how it looks like(of course it will be different data but you see the point):
Lets see example of json(how it could be and how i understand your problem):
{
"stats":[
{
"name":"Amount summary",
"value":"2300"
},
{
"name":"Mortgage",
"value":"1100"
}
],
"success":true,
"totalCount":2,
"items":[
{
"Amount":"1000",
"Delta":"3",
"Mortgage":"500"
},{
"Amount":"1300",
"Delta":"4",
"Mortgage":"600"
}
]
}
Model:
Ext.define('Application.model.MyModel', {
extend: 'Ext.data.Model',
fields: [{
name: 'Amount',
type: 'string'
},{
name: 'Delta',
type: 'string'
},{
name: 'Mortgage',
type: 'string'
}]
});
Store:
var writer = new Ext.data.JsonWriter({
type: 'json',
encode: false,
listful: true,
writeAllFields: true,
returnJson: true
});
var reader = new Ext.data.JsonReader({
type: 'json',
totalProperty: 'totalCount',
rootProperty: 'items',
successProperty: 'success'
});
var proxy = new Ext.data.HttpProxy({
reader: reader,
writer: writer,
type: 'ajax',
url: 'urlForJson',
headers: {
'Content-Type': 'application/json; charset=UTF-8'
}
});
Ext.define('Application.store.MyStore', {
extend : 'Ext.data.Store',
storeId : 'MyStore',
model : 'Application.model.MyModel',
autoLoad: false,
autoSync: true,
proxy:proxy
});
Lets define controller that is going to handle our summary/statistics:
Ext.define('Application.controller.SummaryController', {
extend: 'Ext.app.Controller',
refs: [
{
selector: 'summaryPanel',
ref: 'summaryPanel'
}
],
init: function () {
var controller = this;
controller.listen({
controller: {
'*': {
addSummary: 'addSummary'
}
}
});
},
/**
* Add summary.
*/
addSummary: function addSummary(summary) {
var controller = this;
var summaryPanel= controller.getSummaryPanel();
summaryPanel.removeAll();
Ext.Object.each(stats, function(property, stat){
var labelFieldName = new Ext.form.Label({
text: stat.name+': '
});
var labelFieldValue = new Ext.form.Label({
text: stat.value+' ',
cls: 'bold-item'
});
summaryPanel.items.add(labelFieldName);
summaryPanel.items.add(labelFieldValue);
});
summaryPanel.up('window').doLayout();
}
});
And this is our SummaryPanel:
Ext.define('Application.view.SummaryPanel', {
extend: 'Ext.Panel',
alias: 'widget.summaryPanel',
xtype: 'summaryPanel',
layout: 'anchor',
bodyStyle: 'padding: 5px 5px;',
items: []
});
In next step we are going to load our data to grid and after that we will fill our SummaryPanel with statistics/summary:
Ext.define('Application.controller.GridController', {
extend: 'Ext.app.Controller',
init: function () {
var controller = this;
controller.control({
'gridControlPanel': {
'afterrender': function () {
controller.loadStore();
}
}
})
},
loadStore: function () {
var controller = this;
var store = Ext.getStore('Application.store.MyStore');
store.load({
callback: function (records, operation, success) {
var data = Ext.JSON.decode(operation._response.responseText);
if (!Ext.isEmpty(data.stats)) {
//After loading data, make statistics.
controller.fireEvent('addSummary', data.stats);//This event is handled by SummaryController
}
}
});
}
});
And this UI that cover all above:
Ext.define('Application.view.GridWindow', {
extend: 'Ext.window.Window',
alias: 'widget.gridWindow',
xtype: 'gridWindow',
addMode: false,
autoShow: true,
width: 1200,
height: 700,
resizable: true,
layout: 'fit',
items: [{
xtype: 'grid',
store: 'Application.store.MyStore',
dockedItems: [{
xtype: 'pagingtoolbar',
dock: 'bottom',
store: 'Application.store.MyStore',
displayInfo: true
}]
}],
dockedItems: [{
xtype: 'summaryPanel'//This is our summary panel that will show your summarize.
}]
});
You can use the ExtJS Grid feature -
Ext.grid.feature.Summary
to display summary at the bottom of grid.
Here is the simple example to answer your question -
https://fiddle.sencha.com/#fiddle/tq7
In example, i have directly used the summary object in summaryRenderer of column.
So, for a summary row, you've got the Summary feature. You can stick it in the bottom of your grid with the dock: 'bottom' option. And, finally, to use remotely computed data for the summary, you've got the remoteRoot option. Voilà.
... Except the thing is apparently half baked and remoteRoot is really only used with grouping. Oops. "No need to handle the simple case", they must have thought.
Here's an override that fills the missing part you're needing (fiddle):
Ext.define('Ext.grid.feature.RemoteSummary', {
override: 'Ext.grid.feature.Summary',
// adds 'remote' summary type
getSummary: function (store, type, field, group) {
if (type === 'remote') {
return this.getRemoteSummaryRecord(store).get(field);
} else {
return this.callParent(arguments);
}
},
// helper for our custom summary type, mainly copied from:
// http://docs.sencha.com/extjs/4.2.3/source/AbstractSummary.html#Ext-grid-feature-AbstractSummary-method-generateSummaryData
getRemoteSummaryRecord: function(store) {
if (!this.remoteSummaryRecord) {
var reader = store.proxy.reader,
remoteRoot = this.remoteRoot,
root;
if (remoteRoot && reader.rawData) {
root = reader.root;
reader.root = remoteRoot;
reader.buildExtractors(true);
this.remoteSummaryRecord = reader.read(reader.rawData).records[0];
if (!reader.convertRecordData) {
reader.buildExtractors();
}
reader.root = root;
reader.buildExtractors(true);
}
}
if (!this.remoteSummaryRecord) {
this.remoteSummaryRecord = store.model.create({});
}
return this.remoteSummaryRecord;
},
// ensure our remoteSummaryRecord stays fresh
onStoreUpdate: function() {
delete this.remoteSummaryRecord;
return this.callParent(arguments);
}
});
You would then use it like this:
Ext.create('Ext.grid.Panel', {
features: [{
ftype: 'summary',
remoteRoot: 'summary', // summary key in the server response
dock: 'bottom'
}],
columns: [{
text: 'Age',
dataIndex: 'age',
summaryType: 'remote'
}]
//...
});
With some data of this form:
{
"summary": {
"age": 95,
},
"items": [/*...*/]
}
(And, yes, summing ages makes a lot of sense)

Adding validation to kendo grid destroy button

My datasource grabs rows using a controller method (ASP MVC). I need to set something up so the user can not delete all the rows from a grid, so when the delete button for the final row in the gris id clicked, it needs to realize it is the last, and just say no. I have been trying to use the DataSource.Total() method and here is where I am at so far:
$("#location-list").kendoGrid({
dataSource: ds_locationsList,
sortable: true,
height: "150px",
width: "300px",
editable: "inline",
columns: [{
field: "LocationName", title: "Trespassed Location(s)"
}, {
command: [{
name: "destroy",
text: "Delete",
click: function(){
var rowCount = ds_locationsList.total();
if (rowCount < 1) {
$("#dialog").dialog({
modal: true,
buttons: {
Ok: function () {
$(this).dialog("close");
}
}
});
return false;
}
}
}],
width: "110px"
}]
});
This did not work, I'm thinking I need to get the rowCount from outside the destroy function, maybe in some kind of 'afterLoad'. I have also tried doing it all outside, but in both cases nothing happens:
$(".k-grid-delete").on("click", function () {
var rowCount = ds_locationsList.total();
if (rowCount < 1) {
$("#loclistval").removeClass("hidden");
return false;
}
});
Has anyone had to do this? Any suggestions?
================================EDIT======================================
As noted below, I have tried the custom delete function, but it only removing from the client side. I tried debuggin, but the breakpoint I put in the delete function is never hit, so i must be messing up the call. Here is my transport code:
transport: {
read: {
url: '#Url.Action("JsonPopulateTrespassList", "TrespassOrder")/' + PersId,
dataType: 'json',
type: "POST"
},
destroy: {
url: '#Url.Action("JsonDeleteLocation", "TrespassOrder")',
dataType: 'json',
type: "POST"
}
},
and my parameter map:
parameterMap: function (options, operation) {
if (operation == "destroy" && options.models) {
var values = {};
values["TrespassLocId"] = options.models[0].TrespassLocId;
return values;
}
},
The custom delete:
function locDelete(e) {
var len = this.dataSource.data().length;
if (len === 1) {
alert("There must be at least one location.");
}
else {
this.removeRow($(e.target).closest("tr"));
}
}
and the grid code:
$("#trespassed-location-list").kendoGrid({
dataSource: ds_locationsList,
sortable: true,
height: "150px",
width: "300px",
editable: "incell",
columns: [{
field: "LocationName", title: "Trespassed Location(s)"
}, {
command: [{ name: "destroy", text: "Delete", click: locDelete }],
width: "110px",
}]
});
So it removes the row from the client side, but not the server side. But when I try to debug, the breakpoint on the locDelete function is never hit, so I'm sure what is going on.
The problem is that the remove event is triggered while the row is being deleted and too late for stopping it.
So, the easiest way is defining a custom command that does the validation.
Define Grid commands as:
columns : [
{
command: [
...,
{ name: "Remove", click: obDelete }
],
...
},
...
]
and then define obDelete as:
function obDelete(e) {
var len = this.dataSource.data().length;
if (len === 1) {
alert("last");
} else {
this.removeRow($(e.target).closest("tr"));
}
}
Running example here: http://jsfiddle.net/OnaBai/bxxqC/

Adding a column to a dstore backed dgrid

I have a grid with five columns - username, email, enabled, locked and remove.
Username, email, enabled and locked are sourced from the server. Remove is a client-side element used to indicate that a row should be removed.
I would like to either inject the default value of remove in the store on the client side as the grid content is loading, or set it as the user interacts with the CheckBox widget.
How can I catch the code which is requesting the objects from the server and add another column?
Or, is there a better way to do this.
var TrackableRest = declare([Rest, SimpleQuery, Trackable]);
var store = new TrackableRest({target: '/api/users', useRangeHeaders: true, idProperty: 'username'});
aspect.after(store, "fetch", function (deferred) {
return deferred.then(function (response) {
response.remove = false;
return json(response);
})
});
var grid = new (declare([OnDemandGrid, Selection, Editor]))({
collection: store,
className: "dgrid-autoheight",
columns: {
username: {
label: core.username
},
email: {
label: core.email
},
enabled: {
label: core.enabled,
editor: CheckBox,
editOn: "click",
sortable: false,
renderCell: libGrid.renderGridCheckbox
},
locked: {
label: core.locked,
editor: CheckBox,
editOn: "click",
sortable: false,
renderCell: libGrid.renderGridCheckbox
},
remove: {
editor: CheckBox,
editorArgs: {"checked": false},
editOn: "click",
label: core.remove,
sortable: false,
className: "remove-cb",
renderHeaderCell: function (node) {
var inp = domConstruct.create("input", {id: "cb-all", type: "checkbox"});
return inp;
},
renderCell: libGrid.renderGridCheckbox
}
},
selectionMode: "none"
}, 'grid');
In addition, I don't want to send the remove column to the server.
My final implementation was to code the remove column like so:
remove: {
editor: CheckBox,
label: core.remove,
sortable: false,
className: "remove-cb",
renderHeaderCell: function (node) {
var inp = domConstruct.create("input", {id: "cb-all", type: "checkbox"});
return inp;
}
}
The code to perform the removes is as follows:
var removeBtn = new Button({
label: core.remove
}, 'user-remove-btn');
removeBtn.startup();
removeBtn.on("click", function (event) {
var markedForDeletion = query(".dgrid-row .remove-cb input:checked", "user-grid");
if( markedForDeletion.length > 0 ) {
lib.confirmAction(core.areyousure, function () {
markedForDeletion.forEach(function (node) {
var row = grid.row(node);
store.remove(row.data.username);
});
});
}
});
Thus the remove column became a client-side only control that was handled by the grid and the event handler.

Resources