I have a kendo ui grid that is populated using a datasource that has less than 40 items in it. Each item has about 10 fields one of which is an array that contains no more than 3 items. The grid has a details template that also contains a grid that is populated by the array field from the main record.
When I call dataSource.remove(item), it takes about 10 seconds before the item is removed.
Here is the datasource:
var ds = new kendo.data.DataSource({
transport: {
read: function (options) {
$.ajax({
url: 'some service url',
type: 'GET'
}).success(function (data) {
options.success(data);
})
},
},
sort: { field: 'Ordinal', dir: 'asc' },
schema: {
model: {
id: 'ID',
fields: {
Name: { editable: false },
Ordinal: { editable: false }
}
}
}
})
Here is my grid:
<div id="grd" data-role="grid"
data-auto-bind="true"
data-editable="true"
data-selectable="false"
data-resizable="true"
data-sortable="true"
data-scrollable="true"
data-detail-template="fTemplate"
data-columns="[
{ field: 'Name', title: 'Name' },
{ template: kendo.template($('#tStatus').html()), title: 'Status', width: '150px' },
{ template: kendo.template($('#tError').html()), title: 'Continue On Error', width: '150px' },
{ field: 'Ordinal', title: 'Order', width: '75px' }
]"
data-bind="source: ds, events: { detailInit: initializeDetails }" style="height: 300px;">
</div>
I am struggling to understand why the slow performance. I have used the remove method before and never had a problem.
Any suggestions are highly appreciated.
Thanks.
After several hours of playing around and testing, I found the reason for the slow performance.
The problem was that my grid has 2 template columns, one is a drop down, the other one a checkbox. If I remove those, everything works well. I guess the performance hit come from the fact that the grid has to render the 2 templates and initiate and bind the widgets for each row.
Is there way to improve performance in this scenario?
Related
How can I display fixed number of rows in kendo grid regardless of window size change?
I need to display 10 rows regardless of window resizes.
In order to do this, I need to re-calculate the number of rows and display 10 rows only back inside the grid. My requirement is to calculate the rows to display correctly in the grid. (in fact, I have search on internet and seems can't find any examples/articles, if you have any please share with me too ). My current solution has no error in my opinion, but still incorrect??? . Can someone help me find errors and correct me? Moreover if I insert some items above grid, the wrapper and data items(grid) becomes incorrect totally.
I need some insight and/or code-examples on this matter.
Much appreciated for the help.
My current codes for the resizing grid is as follow:
function resizeGrid() {
var gridElement = $("#grid"),
dataArea = gridElement.find(".k-grid-content"),
gridHeight = gridElement.innerHeight(),
otherElements = gridElement.children().not(".k-grid-content"),
otherElementsHeight = 0;
otherElements.each(function(){
otherElementsHeight += $(this).outerHeight();
});
dataArea.height(gridHeight - otherElementsHeight);
}
function resizeWrapper() {
$("#outerWrapper").height($('#body').innerHeight());
}
and call the functions of both of them:
$(window).resize(function() {
resizeWrapper();
resizeGrid();
});
This is my datasource and schema:
$(document).ready(function() {
$(window).trigger("resize");
$("#grid").kendoGrid({
dataSource: {
data: [
{ userId: 11111, No: 1, isActive:true, date: new Date('12/12/2005 12:30 AM'), from: 'A', sm: 'testing', art: 'Shipped on', due: new Date('12/19/2005 11:30 PM')},
{ userId: 22340, No: 2, isActive:true, date: new Date('12/12/2005 12:30 AM'), from: 'A', sm: 'test0', art: 'Shipped on', due: new Date('12/19/2005 11:30 PM')},
.....
],
schema: {
model: {
fields: {
userId: { editable: true },
date: { filterable: { ui: "datetimepicker"}},
isActive:false,
No: { editable: true }
}
}
},
pageSize: 10
},
sortable: true,
reorderable: true,
scrollable: true,
resizable: true,
filterable: true,
columnMenu: true,
pageable: {
input: true,
numeric: false
},
columns: [
{ field: 'No' ,width: 50 },
{ field: 'userId', title: 'ID', template:"<span class=ul>#=userId#</span> </br> <span class='pie'>1/5</span>", width:60 } ,
{ field: "isActive",
headerTemplate: '!',
template: '<span class="k-icon k-i-circle Unicode: e308" style="color: green; font-size: 28px;"></span> </br> <div class="div1"></div>', filterable:false , width: 40 },
......
]
});
});
Here is my kendo link: http://dojo.telerik.com/aviQU/6
Why not tell the grid to use a page size of 10? No need to recalculate sizes.
Set the data source's page size when you configure the grid.
Or are you setting (CSS/JS) the height of the grid somewhere that is causing it to resize?
Is it possible to persist grid sort/filter/selection on grid.refresh() in some smart optimized way? I need to refresh grid on window resize event to adjust to a new window size. I guess refresh internally destroys and recreates grid, not accounting for possible active sort/filter/selection. Because grid can contain a lot of data (virtual scrolling), I would like to a avoid unnecessary db querying, rendering and sorting. I guess I am looking for a refresh which will refresh on existing data.
Seams like they just implemented it - here is the example.
Maybe to be included in the next release.
Here is the code in the example that does this:
jQuery(function ($) {
$("#grid").shieldGrid({
dataSource: {
data: gridData,
schema: {
fields: {
id: { type: Number },
name: { type: String },
company: { type: String },
phone: { type: String },
age: { type: Number },
gender: { type: String }
}
},
filter: {
// create the initial filter in that form
and: [
{ path: "name", filter: "con", value: "John" }
]
}
},
filtering: {
enabled: true
},
paging: true,
columns: [
{ field: "id", width: "250px", title: "ID" },
{ field: "name", title: "Person Name", width: "250px" },
{ field: "company", title: "Company" },
{ field: "phone", title: "Phone", width: "250px" },
{ field: "age", title: "Age" }
]
});
});
I found the solution in refresh method it self. It accepts options objects where one can provide current data source options to persist. Example to persist sort and/or filter:
var options = {
dataSource: $("#grid").swidget().dataSource
}
$("#grid").swidget().refresh(options);
Please stand me correct if I am wrong here. For selections I guess one can retrieve selected indices and reselect after calling refresh.
EDIT: filter and sort are preserved, but filter row resets (loses all active input values). Could this be a bug? How to keep values in filter row?
I'm quite new using kendo grids.
So far I've managed to do a few stuff and got a workaround for all my problems.
I have a grid with 2 columns.
One column is a product code that the user should enter, and the other is product quantity that should be filled automatically after the user enter the product code. This should be done on change event.
The product quantity is obtained by a service.
So far I have the following code:
var dataSource = new kendo.data.DataSource({
batch: false,
autoSync: false,
data: [],
pageSize: 20,
schema: {
model: {
id: "ProductID",
fields: {
ProductCode: { type: "string", validation: { required: true } },
ProductQuantity: { type: "number", validation: { required: false, editable: false } }
}
}
},
edit: function (e) {
if (e.model.isNew() == false) {
$('[name="ProductQuantity"]').attr("readonly", true);
}
},
change: function (e) {
if (e.action == "itemchange") {
debugger;
apModel.getProductQuantities(e.items[0].ProductCode).ifFetched().then(function (data) {
var data = JSON.parse(data.Response);
})
//how to access next cell???
$("#ap-grid").data("kendoGrid").saveRow();
}
}
});
$("#ap-grid").kendoGrid({
dataSource: dataSource,
pageable: false,
height: 550,
toolbar: ["create"],
columns: [
{ field: "ProductCode", title: "Product Code" },
{ field: "ProductQuantity", title: "Quantity" },
{ command: ["edit", "destroy"], title: " ", width: "250px" }],
editable: "inline",
});
I can't find a way how to access the next cell to add the data to it.
Can you give me a hint?
thanks in advance,
André
When you set e.items[0].ProductQuantity in OnChange event handler grid cell should automatically update value if datasource bind correctly.
According to kendo docs:
e.items - The array of data items that were affected (or read).
That means it reference at original row of datasource.
I would like to use the Kendo grid with remote data virtualization. I also need to have a custom method for the transport.read property.
My grid is configured like this:
$(document).ready(function () {
$("#grid").kendoGrid({
dataSource: {
serverPaging: true,
serverSorting: true,
pageSize: 100,
transport: {
read: function (options) {
// Get the template items, which could be products, collections, blogs or articles
getTemplateItems().then(function (data) {
options.success(data);
});
}
}
},
schema: {
total: function(response) {
return 2000;
}
},
height: 543,
scrollable: {
virtual: true
},
sortable: true,
columns: [
{ field: "title", title: "Title" }
]
});
});
function getTemplateItems() {
var deferred = $q.defer();
smartseoEntityMapping.getEntityInfo({ mappedEntityType: mappedEntityType.Product }).$promise.then(function (data) {
deferred.resolve(data);
});
return deferred.promise;
}
The problem is that the read method is only called once when the grid is initialized. It is not called when the scroll reaches the last item in the current visible set.
I suspect that the grid needs the total number of items but I cannot understand how to set the total number of items. Setting a method for the schema.total property does not work because the method is never called.
So I would like to ask you, is this scenario possible at all, to have the virtualization work with a custom transport.read method, which needs to be called every time to get the next page of data?
Why I am using a custom read? Well I cannot just set an url for the transport.read property because my remote call is made via an angularjs resource, involves setting authentication, etc...
schema is a property of a kendo datasource. It looks like you have it outside of the datasource.
Should be:
$("#grid").kendoGrid({
dataSource: {
serverPaging: true,
serverSorting: true,
pageSize: 100,
transport: {
read: function (options) {
// Get the template items, which could be products, collections, blogs or articles
getTemplateItems().then(function (data) {
options.success(data);
});
}
},
schema: {
total: function(response) {
return 2000;
}
}
},
height: 543,
scrollable: {
virtual: true
},
sortable: true,
columns: [
{ field: "title", title: "Title" }
]
});
I am still new to ST so I am probably doing several things wrong here but I can't figure out where the problems are.
Problem 1
when I use the pull to refresh plug-in, I get double the data instead of it just refreshing the data. I have seen to use a propertyId and so I did to no avail. This should be simple so probably something silly I'm doing wrong.
Problem 2
I am trying to figure out the most efficient way to use the MVC architecture and I have read through the documentation and many examples. So, I don't know if I am just not understanding clearly or need a better example. I am trying to create a simple app for now with a list that I can tap on an item and get a detailed view of that item. I will later evolve it into a more robust monster but for now I am trying to understand the basics. I finally got my close button to close the detail view when I click a list item but then I can no longer get a detail view when tapping another item. I have read that this is due to 'autoDestroy: off' not being present but I tried that, also with no luck. I want to be able to create certain buttons like 'close' that I can put in multiple views and just have to have the logic in the controllers.
Main View
Ext.define('SenchaFirstApp.view.DistributorView', {
// extend: 'Ext.form.Panel',
extend: 'Ext.Container',
requires: ['SenchaFirstApp.store.DistributorStore', 'Ext.dataview.List', 'Ext.Toolbar', 'Ext.form.Panel'],
model: 'SenchaFirstApp.model.Distributors',
alias: 'widget.mainlist',
xtype: 'mainlist',
config:
{
layout: 'fit',
border: 5,
title: 'Distributors',
html: 'My datalist',
autoDestroy: false,
items:[{
xtype: 'toolbar',
docked: 'top',
title: 'Distributor List',
height: 40,
centered: true,
items: [
{
xtype: 'button',
text: 'Close',
width: 100,
height: 20,
name: 'close',
// iconCls: 'delete',
}]
},
{
autoLoad: true,
html: ['<div class="distr"><table><tr><th>Type</th><th>Distributor</th><th>Site</th><th>Status</th><th>Active</th><th>Assigned</th><th>State </th><th>Schedule</th><th>Finished</th></tr></table></div>'],
itemTpl: [ '<div class="distr"><table><tr><td>{t}</td><td>{distr}</td><td colspan="2">{site}</td><td>{status}</td> <td>{active}</td><td>{assigned}</td> <td>{state}</td><td>{schedule}</td><td>{finished}</td></tr></table></div>' ],
xtype: 'list',
store: 'DistrID',
plugins: [
{
xclass: 'Ext.plugin.PullRefresh',
pullRefreshText: 'Pull down to refresh Distributor List'
}],
ui: 'showDetails',
id: 'mainlist',
autoDestroy: false,
}
]
},
});
Detailed View
Ext.define('SenchaFirstApp.view.DetailView',{
extend: 'Ext.Panel',
requires: ['Ext.Toolbar'],
model: 'SenchaFirstApp.model.Distributors',
alias: 'widget.detailview',
xtype: 'detailview',
name: 'detail',
config:{
html: 'This will contain detailed information',
xtype: 'panel',
// fullscreen: false,
centered: true,
modal: false,
scrollable: true,
width: 300,
height: 200,
},
});
Store
Ext.define('SenchaFirstApp.store.DistributorStore', {
extend: 'Ext.data.Store',
requires: ['SenchaFirstApp.model.Distributors'],
config: {
// xtype: 'distrlist',
storeId: 'DistrID',
model: 'SenchaFirstApp.model.Distributors',
autoLoad: true,
proxy: {
type: 'jsonp',
url:'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://gdata.youtube.com/feeds/api/users/hobbitin5/uploads&num=4',
reader: {
type: 'json',
rootProperty: 'responseData.feed.entries'
}
}
}
Controller to get detailed view
Ext.define('SenchaFirstApp.controller.DistributorsController',{
extend: 'Ext.app.Controller',
config:
{
refs:
{
mainlist: '#mainlist',
},
control:
{
mainlist: {
itemtap: 'dispDetail'
},
},
//when item is tapped
listeners: {
itemtap: function(list, index, items, record)
{
console.log('An item was tapped and the listener heard it');
}
}
},
dispDetail: function(view, record) {
console.log('Item was tapped on the Data View');
var oldView = this.getMainlist();
var curRecord = oldView.getStore(record);
var newView = Ext.create('SenchaFirstApp.view.DetailView');
console.log(curRecord);
curRecord += 'other stuff';
newView.setHtml(curRecord);
newView.add(
{
xtype: 'toolbar',
docked: 'top',
title: 'Details',
height: 40,
items: [
{
xtype: 'button',
text: 'Close',
width: 100,
height: 20,
name: 'close',
}]
}
)
oldView.add(newView);
// Ext.Viewport.add(newView)
// Ext.Viewport.setActiveItem(newView)
}
});
Controller for buttons (just close for now)
Ext.define('SenchaFirstApp.controller.NavController', {
extend: 'Ext.app.Controller',
config:
{
refs:
{
mainlist: 'mainlist',
main: '#mainlist',
closeBtn: 'button[name=close]',
detaillist: {
selector: 'panel panel[name=detail] deetaillist',
xtype: 'detailview',
}
},
control:
{
closeBtn: {
tap: 'closeView',
},
mainlist: {
tap: 'closeView',
},
detaillist: {
tap: 'closeView',
}
}
},
closeView: function(){
var newView = this.getMainlist();
// var child = Ext.Viewport.getActiveItem();
var child = this.getDetaillist();
var instance = Ext.getCmp('mainlist');
var activeIndex = instance.indexOf(instance.getActiveItem());
var curIndex = activeIndex+1;
var closeView = this.getDetaillist();
console.log('Close btn clicked' + ' child: ' + this.child + ' activeIndex: ' + activeIndex);
// Ext.Viewport.destroy(closeView); //(child);
newView.remove(child);
newView.destroy();
Ext.Viewport.add(Ext.create('SenchaFirstApp.view.DistributorView'));
` }
})`;
Model
Ext.define('SenchaFirstApp.model.Distributors', {
extend: 'Ext.data.Model',
config: {
idProperty: 'DistrID',
fields: [
{name: 'DistrID'},
{name: 't', type: 'string'},
{name: 'distr', type: 'string'},
{name: 'group', type: 'string'},
{name: 'site', type: 'string'},
{name: 'status', type: 'string'},
{name: 'active', type: 'string'},
{name: 'assigned', type: 'string'},
{name: 'state', type: 'string'},
{name: 'schedule', type: 'string'},
{name: 'finished', type: 'string'},
//{mapping: 't',
// name: 'DistrID'}
],
}
});
I understand that is a lot but any help is appreciated...even a nudge in the right direction. Thanks in advance! Also, I'm sure there is stuff in there that doesn't need to be as I've had trouble locating good examples for what I am trying to accomplish so I have pieces here and there from different examples that I've tried to get to play nice together :(
I solved the refresh issue by changing the idProperty to 'id' and put that in my model field list. I'm still not sure why the 'DistrID wasn't working but I had an 'id' field in my script that held the server response that I didn't know about as I didn't write that part. So, I would have to guess that is why it wasn't working.
I had read that 'autoDestroy: false' is what was needed to allow clicking a second time but that was wrong. When I took that property out it allowed me to continue to open and close details of list items.
Now I just have to work on passing id's of list items clicked to get a detailed view of that particular item. If anyone can help with that it would be great or I will post a new question. Thanks!
I figured out how to get the list item id in case it can help anyone...
Iny my DistributorController I simply get the store and the record id
var store = Ext.getStore('NodeStore');
var id = record.get('id');
Then set the params and load the store
store.getProxy().setExtraParams({id: id});
store.load();
I did also find that you can't reference your store through the controller with refs. Instead get the store as I did above with var store = Ext.getStore('StoreName');
This is all pretty simple stuff but I'm still very knew so maybe it can help out another n00b