JqGrid not able to fill itself from Json response - jqgrid

So I have a javascript function that runs on page load, listed below:
function createGrid()
{
var myGrid =
jQuery("#responseMessages"),
reportBtn = "<input style='height:22px;width:100px;' type='button' value='Report' />",
getColumnIndexByName = function(grid,columnName) {
var cm = grid.jqGrid('getGridParam','colModel');
for (var i=0,l=cm.length; i<l; i++) {
if (cm[i].name===columnName) {
return i; // return the index
}
}
return -1;
};
myGrid.jqGrid({
url: "<%= Url.Action("GetMessages", "Home") %>",
datatype: 'json',
myType: 'GET',
height: 'auto',
colModel: [
{ name:'distance', index:'distance', label:'Distance', width:100 },
{ name:'age', index:'age', label:'Age', width:75 },
{ name:'message', index:'message', label:'Message', width:500 },
{ name:'messageId', index:'messageId', key:true, hidden:true },
{ name:'report', index:'report', label: 'Report', width:100,
formatter:function() { return reportBtn; } }
],
loadComplete: function() {
var i=getColumnIndexByName(myGrid,'report');
// nth-child need 1-based index so we use (i+1) below
$("tbody > tr.jqgrow > td:nth-child("+(i+1)+") > input",myGrid[0]).click(function(e) {
var tr=$(e.target,myGrid[0].rows).closest("tr.jqgrow");
var x=window.confirm("Are you sure you want to report this message?")
if (x)
{
reportMessage(tr[0].id);
}
e.preventDefault();
});
},
rowNum:25,
viewrecords:true,
rowList:[10,25,50],
pager: '#pager',
caption: "What's going on in your area!"
});
}
Now it loads the grid fine, actually makes a call to the public ActionResult GetMessages() on the server correctly, and doesn't receive any data from the response, so it doesn't fill the grid and says there are no records. Yay!
Problem is, I click a button on the page, which triggers this javascript method:
function reloadGrid()
{
$("#responseMessages").trigger("reloadGrid");
}
So the grid goes and re-gets the server method, yay! But this time, the server sends a response back that looks like this from firebug:
{"ContentEncoding":null,"ContentType":null,"Data":{"page":1,"records":2,"rows":[{"id":3,"cell":["\u003c 1 mile","25 hour(s)","sdfgsdfgsdfg","3"]},{"id":2,"cell":["\u003c 1 mile","25 hour(s)","adfg","2"]}],"total":1},"JsonRequestBehavior":1}
However, the grid doesn't fill and still says there are no records, when there should be 3.

You use not standard format of JSON data, so you should include the corresponding jsonReader parameter in the jqGrid which describe how jqGrid should get the data from the JSON input:
jsonReader: {
page: "Data.page",
total: "Data.total",
records: "Data.records",
root: "Data.rows"
}
How you can read from the demo the data will be read after the change.

Related

Slick grid values not populating in the grid

I am trying to populate slick grid it is showing the values while debugging but not showing in the web page.
I have added all the required references. Here is the js function.
function LoadMonthStatus(dropdownYear, buttonId) {
try {
$(".screen").css({ opacity: 0.5 });
$(".slick-cell").css({ opacity: 0.5 });
dirtyFlag = false;
var drpYear = document.getElementById(dropdownYear);
year = drpYear.options[drpYear.selectedIndex].value;
data = [];
var dropdownoptions = ""; // "hard,soft,closed";
var columns = {};
var options = {
editable: true,
enableCellNavigation: true,
asyncEditorLoading: false,
autoEdit: true,
autoHeight: true
};
columns = [
{ id: "month", name: "Month", field: "Month", width: 250 },
{ id: "status", name: "Close Status", field: "Status", options: columnOptions, editor: Slick.Editors.Select, width: 150 }
];
$.ajax({
type: "GET",
url: "http://localhost:51072/PULSE.Service/api/MonthCloseStatus/LoadMonths",
data: { year: $("#drpYear").val() },
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
data = msg;
monthStatusGrid = new Slick.Grid("#slkgrdMonths", data, columns, options);
//grid.setSelectionModel(new Slick.CellSelectionModel());
monthStatusGrid.onCellChange.subscribe(function (e, args) {
var cell = monthStatusGrid.getCellNode(args.row, args.cell);
cell.className += cell.className ? ' slick-cell-modified' : 'slick-cell-modified';
dirtyFlag = true;
});
if (msg == null || msg == "")
document.getElementById(buttonId).disabled = true;
else
document.getElementById(buttonId).disabled = false;
//Enable the button
$(".screen").css({ opacity: 1 });
$(".slick-cell").css({ opacity: 1 });
},
error: function (xhr, textStatus, errorThrown) {
try {
var err = JSON.parse(xhr.responseText);
jAlert(err.Message, 'PULSE');
}
catch (e) {
jAlert(clientErrMessage, 'PULSE');
}
$(".screen").css({ opacity: 1 });
$(".slick-cell").css({ opacity: 1 });
}
});
}
catch (e) {
jAlert(clientErrMessage, 'PULSE');
}
}
Slick grid should be populated with months like Jan, Feb, March and its status respectively in 2 columns.
There are too many variables to give an answer. However I would recommend that you create the grid on page load and just populate it with data on ajax load, rather than creating the grid possibly each time.
In my experience, data not being visible is often CSS related.
Have a look at the samples in the SlickGrid repo, they are pretty comprehensive. Make sure you are using https://github.com/6pac/SlickGrid rather than the old MLeibman repo.
If you want assistance, the best idea is to create a stand alone test page that works with local files (apart from the ajax call) in the example folder of the slickgrid repo.
If you can share that, we can easily reproduce the behaviour. As a bonus, about 90% of the time you'll work out the problem while you are creating the example page.
Check to see
-if the property of data after ajax call is matching with the property provided in columns Field and vice versa.
- if the container u are passing is correct.

jsGrid preload pages ahead

I want to load items by page since I have tables with large amount of data, but I don't want to load items for each page once the user clicks it.
Instead, I rather preload 1000 items (for example) ahead and only fetch more results if the user moves to a page I still didn't fetch the data for.
Is it possible?
I found a way to solve it.
Here is the basic logic:
Create a local data cache object that will hold arrays of results for each page.
When fetching data from the server, always return data for a few pages ahead and store them in the local cache object
Write a method for the controller.loadData that will check to see if you have the desired page results in the local cache object, if so - return that array, if not - return a promise that will fetch the results with some extra data for a few pages ahead.
An example of the local cache object snapshot:
{
"1": [{name: "ff"}, {name: "fdd"}],
"2": [{name: "fds"}, {name: "dsr"}],
"3": [{name: "drr"}, {name: "ssr"}]
}
script section
<script type="text/javascript">
$(document).ready(function () {
List();
});
function List() {
//$(function () {
loadjsgrid();
$("#jsGrid").jsGrid({
height: "auto",
width: "100%",
filtering: true,
editing: false,
sorting: true,
autoload: true,
paging: true,
pageSize: 10,
pageButtonCount: 5,
pageLoading: true,
controller: {
loadData: function (filter) {
var startIndex = (filter.pageIndex - 1) * filter.pageSize;
var d = $.Deferred();
$.ajax({
type: 'GET',
url: '#Url.Action("[ActionName]", "[Controllername]")',
data: filter,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (data) {
if (data.Message == "Failed") {
data.Result = [];
data.Count = 0;
}
console.log(data);
d.resolve(data);
}
});
return d.promise().then(function (q) {
return {
data: q.Result,
itemsCount: q.Count
}
});
}
},
},
fields: [
{ name: "rct_no", type: "text", title: 'Serial Number', autosearch: true, width: '10%' }
],
});
$("#pager").on("change", function () {
var page = parseInt($(this).val(), 10);
$("#jsGrid").jsGrid("openPage", page);
});
}
Controller section
public ActionResult getList(int pageIndex = 1, int pageSize = 10)
{
try
{
var query = #" from rd_receipt_header
var irList = DAL.db.Fetch<[className]>(pageIndex, pageSize, #"select * " + query );
var count = DAL.db.ExecuteScalar<int>("select count(*) " + query);
return Json(new { Message = "Success", Result = irList, Count = count }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex) { return Json(new { Message = "Failed", Result = ex.Message }, JsonRequestBehavior.AllowGet); }
I am using ajax to get data from server as ajax format

Kendo UI Grid posting back already Inserted Rows again

I am running into problem, where when an Insert is completed successfully and if i continue to insert another row, in the next insert it is also sending the row that was inserted successfully earlier, so it goes like this.
On the First insert that row is posted back to webAPI and inserted successfully.
On Next Insert Two rows are sent one of them was from first step.
On third Insert it send previous two rows as well as third row and so on.
What could be the cause of this ?
This is the Code in problem.
$(document).ready(function () {
try {
var degreeYearsArray = new Array();
function GetDegreeName(dgID, degreeName) {
for (var i = 0; i < degreeYearsArray.length; i++) {
if (degreeYearsArray[i].dgID_PK == dgID) {
return degreeYearsArray[i].Name;
}
}
return degreeName;
}
var degreeYearModel = {
id: "DGYR_PK",
fields: {
DGID_FK: {
type: "number",
nullable: false,
editable: false
},
Code: {
type: "string",
validation: {
required: true,
minlength: 2,
maxlength: 160
}
},
Year: {
type: "number",
validation: {
required: true
}
},
EffectiveDate: {
type: "date",
validation: true
},
TerminationDate: {
type: "date",
validation: true
}
}
};
var baseURL = "http://localhost:3103/api/Degree";
var degreeYearTransport = {
create: {
url: baseURL + "/PostDegreeYears", // "/PutOrgSchool",
type: "POST",
// contentType: "application/json"
dataType: "json"
},
read: {
url: function () {
var newURL = "";
if (window.SelectedDegree == null)
newURL = baseURL + "/GetDegreeYears"
else
newURL = baseURL + "/GetDegreeYears?degreeID=" + window.SelectedDegree.DGID_PK;
return newURL;
},
dataType: "json" // <-- The default was "jsonp"
},
update: {
url: baseURL + "/PutDegreeYears", //"/PostOrgSchool",
type: "PUT",
// contentType: "application/json",
dataType: "json"
},
destroy: {
url: function (employee) {
return baseURL + "/deleteOrg" + employee.Id
},
type: "DELETE",
dataType: "json",
contentType: "application/json"
},
parameterMap: function (options, operation) {
try {
if (operation != "read") {
options.EffectiveDate = moment(options.EffectiveDate).format("MM-DD-YYYY");
options.TerminationDate = moment(options.TerminationDate).format("MM-DD-YYYY")
}
var paramMap = kendo.data.transports.odata.parameterMap(options);
delete paramMap.$format; // <-- remove format parameter.
return paramMap;
} catch (e) {
console.error("Error occure in parameter Map or Degree.js" + e.message);
}
}
}; //transport
var dsDegreeYears = new kendo.data.DataSource({
serverFiltering: true, // <-- Do filtering server-side
serverPaging: true, // <-- Do paging server-side
pageSize: 2,
transport: degreeYearTransport,
requestEnd: function (e) {
try {
if (e.type == "update"){
$.pnotify({
title: 'Update Sucessful',
text: 'Record was Updated Successfully',
type: 'success'
});
}
if (e.type == "create") {
$.pnotify({
title: 'Insert Sucessful',
text: 'Record was Inserted Successfully',
type: 'success'
});
}
} catch (e) {
console.error("error occured in requestEnd of dsDegreeYears datasource in DegreeYears.js" + e.message);
}
},
schema: {
data: function (data) {
return data.Items; // <-- The result is just the data, it doesn't need to be unpacked.
},
total: function (data) {
return data.Count; // <-- The total items count is the data length, there is no .Count to unpack.
},
model: degreeYearModel
}, //schema
error: function (e) {
var dialog = $('<div></div>').css({ height: "350px", overflow: "auto" }).html(e.xhr.responseText).kendoWindow({
height: "300px",
modal: true,
title: "Error",
visible: false,
width: "600px"
});
dialog.data("kendoWindow").center().open();
},
});
$("#" + gridName).kendoGrid({
dataSource: dsDegreeYears,
autoBind: false,
groupable: true,
sortable: true,
selectable: true,
filterable: true,
reorderable: true,
resizable: true,
columnMenu: true,
height: 430,
editable: "inline",
toolbar: ["create"],
pageable: {
refresh: true,
pageSizes: true,
buttonCount: 5
},
columns: [ {
field: "DGID_FK",
title: "Degree Name",
width: 140,
template: function (dataItem) {
if (window.SelectedDegree != null) {
dataItem.DGID_FK = window.SelectedDegree.DGID_PK;
return window.SelectedDegree.Name;
}
else
return "";
}
},
{
field: "Code",
title: "Code",
width: 140
},
{
field: "Year",
title: "Year",
width: 140
},
{
field: "Description",
width: 110
},
{
field: "EffectiveDate",
width: 110,
format: "{0:MM/dd/yyyy}"
},
{
field: "TerminationDate",
width: 110,
format: "{0:MM/dd/yyyy}"
},
{
command: ["edit"] , title: " ", width: "172px"
}
]
}); //grid
//Hide history pull-down menu in the top corner
$.pnotify.defaults.history = false;
$.pnotify.defaults.styling = "bootstrap";
// styling: 'bootstrap'
//styling: 'jqueryui'
} catch (e) {
console.error("Error occured in DegreeYears" + e.message);
}
}); // document
This is the Response that is sent from WebAPI
{"$id":"1","DGYR_PK":27,"DGID_FK":64,"Year":4,"Code":"4THYR","EffectiveDate":"2014-01-11T00:00:00","TerminationDate":"2014-01-11T00:00:00","CreatedByUserID_FK":1,"DateCreated":"0001-01-01T00:00:00","UpdatedByUserID_FK":1,"DateUpdated":"0001-01-01T00:00:00","RowStatusID_FK":1,"Degree":null,"DegreeYearExamSchedules":[],"User":null,"RowStatu":null,"User1":null,"DegreeYearSubjects":[]}
So i do see i am returning ID as suggested by the users in response to the question.
still wondering
After you have inserted a record, you need to return the id of that row, otherwise grid consider the previous row as a new row too.
In your create function you call the web API
baseURL + "/PostDegreeYears", // "/PutOrgSchool",
In the server side consider the below code.
public void Create(ABSModel model)
{
try
{
using (context = new Pinc_DBEntities())
{
tblAB tb = new tblAB();
tb.ABS = model.ABS;
tb.AreaFacility = model.AreaFacility;
context.tblABS.Add(tb);
Save();
model.ABSID = tb.ABSID;//this is the important line of code, you are returning the just inserted record's id (primary key) back to the kendo grid after successfully saving the record to the database.
}
}
catch (Exception ex)
{
throw ex;
}
}
please adjust the above example according to your requirement.
This will happen if you don't return the "DGYR_PK" value of the newly inserted model. The data source needs a model instance to have its "id" field set in order not to consider it "new". When you return the "ID" as a response of the "create" operation the data source will work properly.
You can check this example for fully working Web API CRUD: https://github.com/telerik/kendo-examples-asp-net/tree/master/grid-webapi-crud
Here is the relevant code:
public HttpResponseMessage Post(Product product)
{
if (ModelState.IsValid)
{
db.Products.AddObject(product);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, product);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = product.ProductID }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
Your primary key cannot be 0 or null. If you are using auto-incrementing values then you should invoke the "re-read" of your dataSource after post. Check the values and make sure your data values are >0. Note: I have set the default value of the PK in the model to -1 in the column model to help with this.
You can attached and respond to the requestEnd event on the DataSource.
requestEnd: function(e) {
if (e.type === "create") {
this.read();
}
}
What that is saying is: "After you created a record, reread the entries (including the newly created one) into the grid". Thereby giving you the newly created entry with key and all. Of course you see that the extra read may have some performance issue.

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/

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 });
}

Resources