Kendo Grid not updating display correctly - kendo-ui

I'm working on an application where we are using local storage as our data store based on sample from Kendo Code Library. We are currently planning on using functions in the datasource to manipulate the data. You can see that part of our code below.
I have a working sample at http://jsfiddle.net/photo_tom/fL5UC/2/.
var dataSource = new kendo.data.DataSource({
transport: {
create: function (options) {
var localData = JSON.parse(localStorage["grid_data"]);
localData.push(options.data);
localStorage["grid_data"] = JSON.stringify(localData);
options.success(localData);
},
read: function (options) {
var localData = JSON.parse(localStorage["grid_data"]);
options.success(localData);
},
update: function (options) {
var localData = JSON.parse(localStorage["grid_data"]);
for (var i = 0; i < localData.length; i++) {
if (localData[i].ID == options.data.ID) {
localData[i].Value = options.data.Value;
}
}
localStorage["grid_data"] = JSON.stringify(localData);
options.success(localData);
console.log("On update");
console.log(localData);
},
destroy: function (options) {
var localData = JSON.parse(localStorage["grid_data"]);
localData.remove(options.data.ID);
localStorage["grid_data"] = JSON.stringify(localData);
options.success(localData);
}
To see the problem:
Click on "Add New Record". A new record will appear as the first record in the grid
Enter a number in ID column and any text in value column.
Click on "Save Changes" button.
What was the original first row, will overwrite the newly added row
If you click on the JSFiddle run button, then the data will correctly display.
I'm new at using these grids and I don't see what is causing the problem.

When adding a new record to the grid, you need to leave the ID field set to its default value (don't let the user edit it), and assign its unique value within the transport's create function, which should then provide just the created record as a parameter to options.success. So something like:
create: function (options) {
options.data.ID = getNextID();
var localData = JSON.parse(localStorage["grid_data"]);
localData.push(options.data);
localStorage["grid_data"] = JSON.stringify(localData);
options.success(options.data);
},

I have updated below demo.
create: function (options) {
var localData = JSON.parse(localStorage["grid_data"]);
localData.push(options.data);
localStorage["grid_data"] = JSON.stringify(localData);
options.success(localData);
console.log("On create");
console.log(localData);
$("#grid").data('kendoGrid').dataSource.read();
}
http://jsfiddle.net/fL5UC/3/

The issue you are having is because you are calling options.success with the entire dataset as a parameter. For create and update, you only need to return the updated object, as in the options.data object.
options.success(options.data);
See updated sample http://jsfiddle.net/photo_tom/fL5UC/2/
No need to call read on the datasource again, just options.success the updated/created object, and it will act properly.

Related

grid.setOptions(JSON.parse(options)); makes datasource empty

I am trying to save user's grid settings with using localstorage but when loading user settings datasource is empty.
$(document).ready(function () {
var grid = $("#grid").data("kendoGrid");
var options = localStorage["kendo-grid-options-" + "#Request.RawUrl"];
if (options) {
grid.setOptions(JSON.parse(options));
}
$("#save").click(function (e) {
e.preventDefault();
localStorage["kendo-grid-options-" + "#Request.RawUrl"] = kendo.stringify(grid.getOptions());
});
});
Setoptions method was causing empty datasource.Because of this before I call this method I get datasource of grid:
var gridData = $("#grid").data("kendoGrid").dataSource;
After this when I called setoptions and after setoptions when I call this line , it worked like a charm.
grid.setDataSource(gridData);
Here is the final code :
$(document).ready(function () {
var grid = $("#grid").data("kendoGrid");
var gridData = $("#grid").data("kendoGrid").dataSource;
var options = localStorage["kendo-grid-options-" + "#Request.RawUrl"];
if (options) {
grid.setOptions(JSON.parse(options));
grid.setDataSource(gridData);
}
$("#save").click(function (e) {
e.preventDefault();
localStorage["kendo-grid-options-" + "#Request.RawUrl"] = kendo.stringify(grid.getOptions());
location.reload();
});
});

Submitting data to google sheet with multiple tab sheets

I'm referencing this article "How to Submit an HTML Form to Google Sheets…without Google Forms", it worked perfectly for me for only a Google Sheet with one tab.
Need help how to dynamically select what sheet tab the data should be written in the case the google sheet has multiple tabs. I'm using Ajax to submit google sheet btw.
Here's the call by the Ajax:
var $form = $('form#test-form'),
url = 'https://script.google.com/macros/s/MyScript/exec'
$('#submit-form').on('click', function(e) {
e.preventDefault();
var jqxhr = $.ajax({
url: url,
method: "GET",
dataType: "json",
data: $form.serializeObject()
}).success(
// do something
);
})
The code on google sheet web app
function doGet(e){
return handleResponse(e);
}
// Enter sheet name where data is to be written below
var SHEET_NAME = "Sheet1";
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
function handleResponse(e) {
// shortly after my original solution Google announced the LockService[1]
// this prevents concurrent access overwritting data
// [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
var sheet = doc.getSheetByName(SHEET_NAME);
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
// loop through the header columns
for (i in headers){
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
row.push(e.parameter[headers[i]]);
}
}
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
return ContentService
.createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
.setMimeType(ContentService.MimeType.JSON);
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e}))
.setMimeType(ContentService.MimeType.JSON);
} finally { //release lock
lock.releaseLock();
}
}
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
}
If there is a way to change the variable SHEET_NAME dynamically then I think it will be good. Thanks

Knockout predefined default with options binding to observable array

I am getting a list of options for a select from a server and populating an observableArray. Then I would like to set the selected item to a predefined value. I have a very simple jsFiddle that emulates pulling data from a server with a button click.
http://jsfiddle.net/JonathanN/hev1rqeu/
Here's the Javascript with the basic attempt:
(function() {
var vm = (function() {
var self = this;
self.array = ko.observableArray([]);
self.selectedValue = ko.observable();
self.useSetTimeout = ko.observable(false);
self.array.subscribe(function(newValue) {
self.selectedValue('b');
});
self.populate = function() {
self.array(['a','b','c']);
};
}());
ko.applyBindings(vm);
}());
And here's my workaround, which replaces "self.selectedValue('b');":
var waitForSelectToPopulate = function() {
self.selectedValue('b');
if(self.selectedValue() != 'b') {
setTimeout(waitForSelectToPopulate, 10);
}
};
waitForSelectToPopulate();
I am not very fond of this as a workaround. It seems like there should be a reasonable way to handle this, but just setting the value on subscribe trigger doesn't seem to work.
You need optionsAfterRender. Here's a fiddle:
http://jsfiddle.net/sifriday/hev1rqeu/4/
HTML -
<select data-bind="options: array, value: selectedValue, optionsAfterRender: setVal">
JS addition -
self.setVal = function() {
self.selectedValue('b');
}
Docs - http://knockoutjs.com/documentation/options-binding.html - and scroll down to Note 2
Once the populate event has gone and got the json and placed it into your array, why not just set the value right after? as soon as you set the data inside of self.array it will update.
(function() {
var vm = (function() {
var self = this;
self.array = ko.observableArray([]);
self.selectedValue = ko.observable();
self.populate = function() {
// magical assmagic goes and get's json, and converts it to ['a','b','c']
self.array(['a','b','c']); // dropdown is now populated
self.selectedValue('c'); // therefore we can set it to a valid value
};
}());
ko.applyBindings(vm);
}());
see the following:
http://jsfiddle.net/hev1rqeu/5/

How to programmatically create a new row and put that row in edit mode in Kendo grid

In my Kendo grid I am trying to put the 'create new item' button in the header (title) of the command column instead of the toolbar. Here is part of my grid definition:
var grid = $("#grid").kendoGrid({
columns: [{ command: { name: "edit", title: "Edit", text: { edit: "", cancel: "", update: "" } },
headerTemplate: "<a onclick ='NewItemClick()' class='k-button k-button-icontext k-create-alert' id='new-item-button' title='Click to add a new item'><div>New Item</div></a>"},
My question is: how to create a new row and put that row in edit mode in 'NewItemClick()'
There are some troublesome scope issues when you try to bind the click event in the template definition itself.
Instead, it is easier to assign the link an ID, and then bind the click event later. Notice that I've given it id=create.
headerTemplate: "<a id='create' class='k-button k-button-icontext k-create-alert' id='new-item-button' title='Click to add a new item'><div>New Item</div></a>"
Then in document ready, I bind the click event:
$("#create").click(function () {
var grid = $("#grid").data("kendoGrid");
if (grid) {
//this logic creates a new item in the datasource/datagrid
var dataSource = grid.dataSource;
var total = dataSource.data().length;
dataSource.insert(total, {});
dataSource.page(dataSource.totalPages());
grid.editRow(grid.tbody.children().last());
}
});
The above function creates a new row at the bottom of the grid by manipulating the datasource. Then it treats the new row as a row "edit". The action to create a new row was borrowed from OnaBai's answer here.
Here is a working jsfiddle, hope it helps.
I would like to complete on gisitgo's answer. If your datasource takes some time to update, when calling page(...), then the refresh of the grid will cancel the editor's popup. This is averted by binding the call to editRow to the "change" event :
var grid = $("#grid").data("kendoGrid");
if (grid) {
//this logic creates a new item in the datasource/datagrid
var dataSource = grid.dataSource;
var total = dataSource.data().length;
dataSource.insert(total, {});
dataSource.one("change", function () {
grid.editRow(grid.tbody.children().last());
});
dataSource.page(dataSource.totalPages());
}
NB: This approach will yield problems if your grid is sorted because the new row will not necessarily be at the end
I have found that issues might appear if you have multiple pages, such as the inserted row not opening up for edit.
Here is some code based on the current index of the copied row.
We also edit the row based on UID for more accuracy.
function cloneRow(e) {
var grid = $("#grid").data("kendoGrid");
var row = grid.select();
if (row && row.length == 1) {
var data = grid.dataItem(row);
var indexInArray = $.map(grid.dataSource._data, function (obj, index)
{
if (obj.uid == data.uid)
{
return index;
}
});
var newRowDataItem = grid.dataSource.insert(indexInArray, {
CustomerId: 0,
CustomerName: null,
dirty: true
});
var newGridRow = grid.tbody.find("tr[data-uid='" + newRowDataItem.uid + "']");
grid.select(newGridRow);
grid.editRow(newGridRow);
//grid.editRow($("table[role='grid'] tbody tr:eq(0)"));
} else {
alert("Please select a row");
}
return false;
}

Ext JS 4 - how to create multiple store instances and assign to views? (MVC)

How do you create unique instances of stores and assign them to views (I am ok with creating unique views and/or controllers if that's required)?
A simple use case - I want to open multiple grid's (of the same type) with a list of records from a store in each. Each grid would need to have it's own store instance, because it could have it's own list of records, it's own filtering, etc. etc.
I tried this, but it does not work, my grids do not draw:
var theView = Ext.create('App.view.encounter.List');
theView.title = 'WORC Encounters';
var theStore=Ext.create('App.store.Encounters');
theView.store=theStore;
tabhost.add({title:'WORC',items:theView});
var theView = Ext.create('App.view.encounter.List');
theView.title = 'NC Encounters';
var theStore2=Ext.create('App.store.Encounters');
theView.store=theStore2;
tabhost.add({title:'NC',items:theView});
You need to assign the store when the component is initializing (or before). In the initComponent.
Ext.define('classname', {
extend: 'Ext.grid.Panel',
//...
initComponent: function() {
var me = this;
var theStore = Ext.create('App.store.Encounters');
Ext.apply(me, {
store: theStore
});
me.callParent();
}
//...
});
You could also do it this way:
//Create the store
var theStore = Ext.create('App.store.Encounters');
//Create the view
var theView = Ext.create('App.view.encounter.List', {
store: theStore
});
Edit for you example specific:
var theStore = Ext.create('App.store.Encounters');
var theView = Ext.create('App.view.encounter.List', {
title: 'WORC Encounters',
store: theStore
});
tabhost.add({title:'WORC',items:theView});
var theStore2=Ext.create('App.store.Encounters');
var theView2 = Ext.create('App.view.encounter.List', {
title: 'NC Encounters',
store: theStore2
});
tabhost.add({title:'NC',items:theView2});

Resources