Kendo Grid: Canceling edit deletes new row - javascript

Here is a demo to for the behavior I am experiencing.
If you edit the existing row with id 1, change the text to something else and then press the cancel button, the row is reverted correctly to its previous state.
In order to reproduce my problem you need to:
Add a new row
Press the update button to save it.
Select the row again and press the update button.
Press the cancel button
The row disappears!
Even though there are similar questions on this problem, I have yet to find a satisfactory answer.
Some people say that I need to define an id. As you can see from my demo, this does not make any difference, the new row has an id and it still disappears.
There are some suggestions when you are using a remote datasource, but this does not work in my case, I need to use local data.
Finally, there is this answer. While it does prevent the new row from disappearing, Canceling the row does not revert the data to its old state, it only closes the editor and the data are as they where after the edit.

Had the same problem. I had it solved by simply calling the cancelChanges() method of the DataSource:
..
cancel: function(e) {
$('#mainGrid').data('kendoGrid').dataSource.cancelChanges();
},
..

It seems like bug only.But still you could acheive desired output through the below code.
I have introduced new variable tempSavedRecords and update
that variable when you are save or delete the record with kendo
datasource data.
When the user clicks cancel button fill the kendo datasource with tempSavedRecords.
$(document).ready(function() {
var tempSavedRecords = null;
var gridDataSource = new kendo.data.DataSource({
data: [
{ id: 1, description: 'Test 1', begin: new Date() }
],
schema: {
model: {
id: 'id',
fields: {
id: { type: 'number' },
description: { type: 'string' },
begin: { type: 'date' }
}
}
}
});
$('#grid').kendoGrid({
dataSource: gridDataSource,
scrollable: true,
sortable: true,
toolbar: ['create'],
columns: [
{ field: 'id', title: 'ID', width: 'auto' },
{ field: 'description', title: 'Description', width: 'auto' },
{ field: 'begin', title: 'Begin', width: 'auto', format: '{0:d}' },
{ command: ['edit', 'destroy'], title: ' ', width: '172px'}],
editable: {
mode: 'inline',
confirmation: false
},
save:function(e){
updateTempRecords();
},
cancel:function(e){
if(tempSavedRecords != null){
$('#grid').data('kendoGrid').dataSource.data(tempSavedRecords);
}
else{
$('#grid').data('kendoGrid').dataSource.cancelChanges();
}
},
remove:function(e){
$('#grid').data('kendoGrid').dataSource.remove(e.model)
updateTempRecords();
}
});
function updateTempRecords(){
tempSavedRecords = $('#grid').data('kendoGrid').dataSource.data();
tempSavedRecords = tempSavedRecords.toJSON();
}
});
Hope this helps.Thanks.
$(document).ready(function() {
var tempSavedRecords = null;
var gridDataSource = new kendo.data.DataSource({
data: [
{ id: 1, description: 'Test 1', begin: new Date() }
],
schema: {
model: {
id: 'id',
fields: {
id: { type: 'number' },
description: { type: 'string' },
begin: { type: 'date' }
}
}
}
});
$('#grid').kendoGrid({
dataSource: gridDataSource,
scrollable: true,
sortable: true,
toolbar: ['create'],
columns: [
{ field: 'id', title: 'ID', width: 'auto' },
{ field: 'description', title: 'Description', width: 'auto' },
{ field: 'begin', title: 'Begin', width: 'auto', format: '{0:d}' },
{ command: ['edit', 'destroy'], title: ' ', width: '172px' }
],
editable: {
mode: 'inline',
confirmation: false
},
save:function(e){
updateTempRecords();
},
cancel:function(e){
if(tempSavedRecords != null){
$('#grid').data('kendoGrid').dataSource.data(tempSavedRecords);
}
else{
$('#grid').data('kendoGrid').dataSource.cancelChanges();
}
},
remove:function(e){
$('#grid').data('kendoGrid').dataSource.remove(e.model)
updateTempRecords();
}
});
function updateTempRecords(){
tempSavedRecords = $('#grid').data('kendoGrid').dataSource.data();
tempSavedRecords = tempSavedRecords.toJSON();
}
});
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.1.318/styles/kendo.common.min.css" />
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.1.318/styles/kendo.default.min.css" />
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="grid"></div>
<script src="http://cdn.kendostatic.com/2014.1.318/js/jquery.min.js"></script>
<script src="http://cdn.kendostatic.com/2014.1.318/js/kendo.all.min.js"></script>
<script src="script.js"></script>
</body>
</html>

This happens because the id remains set to its default value. The data source considers such data items as "new" and cancelling them removes them. Once you save a "new" item you need to set its id to a non-default value.

I modified your changes with this pluckr, and it seems to work.
The only change that I did was to rename 'id' column to 'ided'.
Somehow the 'id' column name, as shown in kendo examples does not work, and to me it seems like a bug.
model: {
id: 'ided',
fields: {
ided: { type: 'number' },
description: { type: 'string' },
begin: { type: 'date' }
}
}

I could resolve this problem by re-setting the data object after add new row.
For example:
function onInsertNewRow(dataItem) {
myDataSource.insert(dataItem);
myDataSource.data(myDataSource.data());
}
By calling data() method you say to grid that the new data asigned is the base data and the new rows are no longer new.
I hope this help you.

Related

KendoUI - Read edited data from grid cells using underlying model

below i have a kendoUI grid that fetches data from a server. The user can then edit two columns in the grid. I have a separate button that will post the data back to the server and i do not use the kendo grid's UPDATE transport for this. The problem i am having is that if i fetch the data from the grid, it does not reflect the user inputs. For example, to get to the underlying data for the grid i do the following:
products= $("#Grid").data("kendoGrid").dataSource.data()
But when i iterate over products and check the NewPrice or Comment property, it's always blank. Here is how the grid's data source is defined:
dataSource: {
transport: {
read: function (options) {
$.ajax({
url: "/Portal/API/GetProductPrices?id=" + pId,
dataType: "json",
success: function (data) {
localModel.userId = data.userId;
localModel.products = data.Products;
return options.success(model.products);
},
});
}
},
},
scrollable: false,
selectable: true,
schema: {
model: {
id: 'Id',
fields: {
Item: { type: 'string', editable: false },
Price: { type: 'number', editable: false },
NewPrice: { type: 'number', editable: true },
Comment: { type: 'string', editable: true, validation: { required: true } },
}
}
},
columns: [
{ field: "Price", title:"Price"},
{
field: "NewPrice", title: "<span class='editMode'>Proposed Value</span>", format: "{0:p}", attributes: { style: "text-align:center;" }, headerAttributes: { style: "text-align:center;" }, width: "50px",
template: "#=NewValueTemplate(data)#",
},
{ field: "Comment", title: "<span class='editMode viewWorkflowMode'>Notes</span>", width: "210px", template: "#=NotesTemplate(data)#" },
]
Any advice in resolving would be appreciated
You haven't specified the editing type that you are using.
Which type are you using: inline, batch or popup ?
Is only this the datasource ? I see no update function.
I suggest you take a look at the three demos.
Batch
Inline
Popup
The worst thing is that you haven't specified the value of the property editable.
By default it is false, that means the kendoGrid is not editable, even if you have specified editable: true over your model fields.
Shortcut to "Editable" configuration
update #2 :
As already said here
If the data source is bound to a remote service (via the transport option) the data method will return the service response.
So, when you use dataSource.data() method on your grid, if you haven't updated correctly your datasource, you should receive all "old" data. (I found strange that you get blank value over those properties, maybe a cache problem)
As I already said, your dataSource doens't provide no update function.
Here you are an example about the configuration of the update function in kendo dataSource, with request to remote service.
Suggest you to look on both examples:
Example - specify update as a string and Example - specify update as a function
Please implement the logic from the following example:
var _roleDataSource = new kendo.data.DataSource({
data: [
{ id: 1, title: "Software Engineer" },
{ id: 2, title: "Quality Assurance Engineer" },
{ id: 3, title: "Team Lead" },
{ id: 4, title: "Manager" }
]
});
var _peopleDataSource = new kendo.data.DataSource({
data: [
{ id: 1, name: "John", roleId: 1, roleTitle: "Software Engineer" },
{ id: 2, name: "Dave", roleId: 2, roleTitle: "Quality Assurance Engineer" },
{ id: 3, name: "Aaron", roleId: 3, roleTitle: "Team Lead" },
{ id: 4, name: "Russell", roleId: 4, roleTitle: "Manager" }
]
});
var _grid = $("#grid").kendoGrid({
dataSource: _peopleDataSource,
columns: [
{
field: "name",
title: "Name"
},{
field: "roleTitle",
title: "Role",
editor: function(container, options) {
$("<input data-bind='value:roleTitle' />")
.attr("id", "ddl_roleTitle")
.appendTo(container)
.kendoDropDownList({
dataSource: _roleDataSource,
dataTextField: "title",
dataValueField: "title",
template: "<span data-id='${data.id}'>${data.title}</span>",
select: function(e) {
var id = e.item.find("span").attr("data-id");
var person =_grid.dataItem($(e.sender.element).closest("tr"));
person.roleId = id;
setTimeout(function() {
$("#log")
.prepend($("<div/>")
.text(
JSON.stringify(_grid.dataSource.data().toJSON())
).append("<br/><br/>")
);
});
}
});
}
}
],
editable: true
}).data("kendoGrid");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="grid"></div>
<br/>
<div id="log"></div>
You may view the demo here: http://jsfiddle.net/khNsE/175/
In this case, i needed to allow some rows based on data rules to enter 'edit mode' at the same time, so specifying inline, popup, etc was not an option. What i did instead was use a custom template function when defining the grid columns. the custom template function returned html, but in the html i used the data-bind attribute to bind to my model. Finally on the DataBound event of the grid, i bind my model to the rows.
field: "NewPrice", title: "New", format: "{0:p}", template: "#=newValueTemplate(d)#",
....
....
function newValueTemplate(d){
if (d.IsEditable)
return "<input type='number' data-bind='value:NewPrice' />"
else
return "<span />"
}
function gridDataBound(e){
var items = this.dataSource.view();
var gridRows = this.tbody.children();
for (var i = 0; i < items.length; i++)
kendo.bind(gridRows[i], items[i]);
}

Ext JS 4 row editing save issues

I'm trying to create a simple grid using Ext JS that allows me to quickly navigate through and edit each row. Basically I want to start editing a row, hit the down arrow and be able to begin editing the next row. It's fine if I don't save until the end, but right now when I try to navigate down to the next field, it tells me I need to commit or cancel the edit first. Is there a setting I'm missing or something I'm just overlooking? I'm still pretty new to Ext JS and don't fully understand how everything works or ties together yet.
Here's my code:
<!DOCTYPE html>
<html>
<head>
<title>Grid Testing</title>
<script type="text/javascript" src="js/ext-4.1.1a/ext-all.js"></script>
<link rel="stylesheet" type="text/css" href="http://cdn.sencha.com/ext-4.1.1a-gpl/resources/css/ext-all-debug.css" />
<style type="text/css">
.x-selectable, .x-selectable * {
-moz-user-select: text!important;
-khtml-user-select: text!important;
}
.x-grid-row-editor-buttons{
display: none;
}
</style>
</head>
<body>
<div id="grid-example"></div>
<script type="text/javascript">
Ext.application({
name: 'myGrid',
autoCreateViewport: false,
launch: function() {
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.util.*',
'Ext.state.*',
'Ext.form.*',
]);
/**
* MODEL
**/
var model = Ext.define('Answer', {
extend: 'Ext.data.Model',
fields: [{
name: 'answer_number',
type: 'float',
convert: null,
defaultValue: undefined
},
{
name: 'answer_text',
type: 'float',
convert: null,
defaultValue: undefined
},
{
name: 'data_column',
type: 'float',
convert: null,
defaultValue: undefined
}],
idProperty: 'answer_number'
});
/**
* STORE
**/
// sample static data for the store
var myData = [[1, 'test answer 1', 12], [2, 'test answer 2', 12], [3, 'test answer 3', 12], [4, 'test answer 4', 12], [5, 'test answer 5', 12]];
// create the data store
var store = Ext.create('Ext.data.ArrayStore', {
model: 'Answer',
data: myData,
autoSync: true,
autoLoad: true,
});
var rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
clicksToEdit: 1,
autoCancel: false,
});
var columns = [{
text: '#',
width: 40,
sortable: false,
dataIndex: 'answer_number'
},
{
text: 'Answer',
flex: 1,
sortable: false,
dataIndex: 'answer_text',
editor: {
allowBlank: true
}
},
{
text: 'Data Column',
width: 75,
sortable: false,
dataIndex: 'data_column'
}];
// create the Grid
var grid = Ext.create('Ext.grid.Panel', {
store: store,
stateful: true,
collapsible: false,
multiSelect: true,
columns: columns,
height: 150,
width: 600,
renderTo: 'grid-example',
plugins: [rowEditing],
}); //end grid
grid.getEl().addKeyMap({
eventName: "keyup",
binding: [
{
// Navigate through the answers or create a new one if at the end
key: Ext.EventObject.DOWN,
fn: function(){
store.commitChanges(); //this doesnt seem to be working
var currentRow = grid.getSelectionModel().getSelection()[0];
var rowCount = store.count();
if((currentRow['index']+1) < rowCount){
//select next row
rowEditing.startEdit((currentRow['index']+1), 1);
}else{
//create new one with dummy data
var r = Ext.create('Answer', {
answer_number: '9',
answer_text: '',
data_column: '50'
});
store.insert(rowCount, r);
rowEditing.startEdit(rowCount, 1)
}
}
},
{
// Navigate up through the answers
key: Ext.EventObject.UP,
fn: function(){
store.commitChanges();
var currentRow = grid.getSelectionModel().getSelection()[0];
rowEditing.startEdit((currentRow['index']-1), 1);
}
}
]
});
},
});
</script>
</body>
</html>
Any help is very much appreciated. Thanks!
Well, I switched to cellEditing instead of rowEditing and now it seems to work fine (with built in tab/shift+tab usage too).

How capture destroy event in kendo ui grids when click is done?

I want to execute an action when click event of a delete button in a grid is done. How can I know when is clicked in Javascript?
(Read IMPORTANT at the end)
Use:
$("tr .k-grid-delete", "#grid").on("click", function(e) {
alert("deleted pressed!");
})
Where #grid is the id of your grid.
If you want to know the data item you can do:
var item = $("#grid").data("kendoGrid").dataItem($(this).closest("tr"));
Alternatively, you can define the command in the grid.columns as:
{
command: [
"edit",
{
name : "destroy",
text : "remove",
click: myFunction
}
],
title : "Commands",
width : "210px"
}
where myFunction is:
function myFunction(e) {
alert("deleted pressed!");
var item = $("#grid").data("kendoGrid").dataItem($(this).closest("tr"));
// item contains the item corresponding to clicked row
}
IMPORTANT: You might want to define you own destroy button where only the styling is copied from the original one (no other actions / checks). If so define your grid.columns.command as:
{
command: [
"edit",
{
name : "destroy",
text : "remove",
className: "ob-delete"
}
],
title : " ",
width : "210px"
}
and then define:
$(document).on("click", "#grid tbody tr .ob-delete", function (e) {
e.preventDefault();
alert("deleted pressed!");
var item = $("#grid").data("kendoGrid").dataItem($(this).closest("tr"));
// item contains the item corresponding to clicked row
...
// If I want to remove the row...
$("#grid").data("kendoGrid").removeRow($(this).closest("tr"));
});
simple. You can make use of remove: to capture destroy event in kendo.
$('#grid').kendoGrid({
dataSource: gridDataSource,
scrollable: true,
sortable: true,
toolbar: ['create'],
columns: [
{ field: 'id', title: 'ID', width: 'auto' },
{ field: 'description', title: 'Description', width: 'auto' },
{ field: 'begin', title: 'Begin', width: 'auto', format: '{0:d}' },
{ command: ['edit', 'destroy'], title: ' ', width: '172px' }
],
editable: {
mode: 'inline',
confirmation: false
},
save:function(e){
alert("save event captured");
//Do your logic here before save the record.
},
remove:function(e){
alert("delete event captured");
//Do your logic here before delete the record.
}
});
$(document).ready(function() {
var gridDataSource = new kendo.data.DataSource({
data: [
{ id: 1, description: 'Test 1', begin: new Date() }
],
schema: {
model: {
id: 'id',
fields: {
id: { type: 'number' },
description: { type: 'string' },
begin: { type: 'date' }
}
}
}
});
$('#grid').kendoGrid({
dataSource: gridDataSource,
scrollable: true,
sortable: true,
toolbar: ['create'],
columns: [
{ field: 'id', title: 'ID', width: 'auto' },
{ field: 'description', title: 'Description', width: 'auto' },
{ field: 'begin', title: 'Begin', width: 'auto', format: '{0:d}' },
{ command: ['edit', 'destroy'], title: ' ', width: '172px' }
],
editable: {
mode: 'inline',
confirmation: false
},
save:function(e){
alert("save event captured");
},
remove:function(e){
alert("delete event captured");
}
});
});
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.1.318/styles/kendo.common.min.css" />
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.1.318/styles/kendo.default.min.css" />
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="grid"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="http://cdn.kendostatic.com/2014.1.318/js/jquery.min.js"></script>
<script src="http://cdn.kendostatic.com/2014.1.318/js/kendo.all.min.js"></script>
<script src="script.js"></script>
</body>
</html>

Ext.grid.Panel - how to access row data

I have an Ext.grid.Panel with a listener for dblclick. It looks like this:
listeners: {
dblclick: {
fn : function() {
console.log("double click event processed");
},
element: 'el'
}
}
When a row is double clicked, I would like to open a URL in a new page. In order to determine the URL, I need access to row data - either that, or to the "row" in my JSON that serves as the Panel's store. How would I access this data?
Well, the event is itemdblclick (no dblclick). And the row is passed as an argumento to the handler.
For example, in the follow sample, when you double click on a row you can see an alert pop-up window displaying the selected Simpson name:
Ext.create('Ext.data.Store', {
storeId:'simpsonsStore',
fields:['name', 'email', 'phone'],
data:{'items':[
{ 'name': 'Lisa', "email":"lisa#simpsons.com", "phone":"555-111-1224" },
{ 'name': 'Bart', "email":"bart#simpsons.com", "phone":"555-222-1234" },
{ 'name': 'Homer', "email":"home#simpsons.com", "phone":"555-222-1244" },
{ 'name': 'Marge', "email":"marge#simpsons.com", "phone":"555-222-1254" }
]},
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'items'
}
}
});
Ext.create('Ext.grid.Panel', {
title: 'Simpsons',
store: Ext.data.StoreManager.lookup('simpsonsStore'),
columns: [
{ text: 'Name', dataIndex: 'name' },
{ text: 'Email', dataIndex: 'email', flex: 1 },
{ text: 'Phone', dataIndex: 'phone' }
],
height: 200,
width: 400,
listeners: {
itemdblclick: {
fn : function(grid, record) {
alert(record.get('name'));
}
}
},
renderTo: Ext.getBody()
});​
You also can see it working here: http://jsfiddle.net/lontivero/utjyd/1/
Good luck!

Trouble with Sencha Touch MVC

I'm trying to learn how to use Sencha Touch to build web apps. I've been following the tutorial Here and I am a bit stuck. Below have created one controller, two views and a model (All other code is copy & paste from the tutorial). The first view, Index works great. However if I try to access the second, it brings up a blank page, none of the toolbar buttons work and it doesn't fire the alert.
If I do comment out the line this.application.viewport.setActiveItem(this.editGyms);, the alert will fire, but obviously, it doesn't render the page.
I've looked at a couple other tutorials, and they seem to also be using the setActiveItem member to switch views.. Am I missing something, or do I have to somehow deactivate the first view to activate the second or something?
HomeController.js
Ext.regController('Home', {
//Index
index: function()
{
if ( ! this.indexView)
{
this.indexView = this.render({
xtype: 'HomeIndex',
});
}
this.application.viewport.setActiveItem(this.indexView);
},
editGyms: function()
{
if ( ! this.editGyms)
{
this.editGyms = this.render({
xtype: 'EditGymStore',
});
}
this.application.viewport.setActiveItem(this.editGyms);
Ext.Msg.alert('Test', "Edit's index action was called!");
},
});
views/home/HomeIndexView.js
App.views.wodList = new Ext.List({
id: 'WODList',
store: 'WODStore',
disableSelection: true,
fullscreen: true,
itemTpl: '<div class="list-item-title"><b>{title}</b></div>' + '<div class="list-item-narrative">{wod}</div>'
});
App.views.HomeIndex = Ext.extend(Ext.Panel, {
items: [App.views.wodList]
});
Ext.reg('HomeIndex', App.views.HomeIndex);
views/home/EditGymStore.js
App.views.EditGymStore = Ext.extend(Ext.Panel, {
html: 'Edit Gyms Displayed Here',
});
Ext.reg('EditGymStore', App.views.EditGymStore);
models/appModel.js
Ext.regModel('WOD', {
idProperty: 'id',
fields: [
{ name: 'id', type: 'int' },
{ name: 'date', type: 'date', dateFormat: 'c' },
{ name: 'title', type: 'string' },
{ name: 'wod', type: 'string' },
{ name: 'url', type: 'string' }
],
validations: [
{ type: 'presence', field: 'id' },
{ type: 'presence', field: 'title' }
]
});
Ext.regStore('WODStore', {
model: 'WOD',
sorters: [{
property: 'id',
direction: 'DESC'
}],
proxy: {
type: 'localstorage',
id: 'wod-app-localstore'
},
// REMOVE AFTER TESTING!!
data: [
{ id: 1, date: new Date(), title: '110806 - Title1', wod: '<br/><br/>Desc1</br><br/>' },
{ id: 1, date: new Date(), title: '110806 - Title1', wod: '<br/><br/>Desc2</br><br/>' }
]
});
viewport.js with toolbar
App.views.viewport = Ext.extend(Ext.Panel, {
fullscreen: true,
layout: 'card',
cardSwitchAnimation: 'slide',
scroll: 'vertical',
styleHtmlContent: true,
style: 'background: #d8e2ef',
dockedItems: [
{
xtype: 'toolbar',
title: 'The Daily WOD',
buttonAlign: 'right',
items: [
{
id: 'loginButton',
text: 'Login',
ui: 'action',
handler: function() {
Ext.Msg.alert('Login', "This will allow you to Login!");
}
},
{
xtype: 'spacer'
},
{
xtype: 'button',
iconMask: true,
iconCls: 'refresh',
ui: 'action',
handler: function() {
Ext.Msg.alert('Refresh', "Refresh!");
}
}]
},
],
});
Thanks for the help!!
In HomeController.js your action function (editGyms) is the same name as the variable you're using for your view (this.editGyms) so when it tries to setActiveItem(this.editGyms) its actually passing the controllers action function rather than the results of this.render({...})
Either change the name of the controller action or change the name of the variable you use to hold the view.. like
editGyms: function() {
if ( ! this.editGymView) {
this.editGymView = this.render({
xtype: 'EditGymStore',
});
}
this.application.viewport.setActiveItem(this.editGymView);
Ext.Msg.alert('Test', "Edit's index action was called!");
}
I am colleagues with the guy that wrote the tutorial you are referring to. You should add a comment on that post because I described your problem to him and he said that he knows the solution.
You can just add a link to this page so that you won't need to describe everything again.
Also he will (very) soon publish the 3rd part of that tutorial that will cover some similar things.

Categories

Resources