Kendo Upload in Kendo grid rows add row - razor

I am using Kendo() grid in my application which has binding with an model.
I want to provide Upload control in Grid against each row. Please see razor view design as follows:
<div id="processDetailGrid"></div>
var processDetailGrid
, processDetailDataSource
, processDetailToolbar
, projectComboBox;
$(function () {
ProcessDetailToolbar = $("#processDetailToolbar").kendoToolBar({items: [{ id: "processDetailAdd", type: "button", spriteCssClass: "fa fa-plus-square", text: "Add", overflow: "never", click: processDetailAdd }]}).data("kendoToolBar");
function processDetailAdd() {
processDetailGrid.addRow();}
processDetailGrid = $("#processDetailGrid").kendoGrid({
dataSource: processDetailDataSource,
height: '98%',
selectable: "cell",
resizable: true,
reorderable: true,
columnMenu: true,
editable: false,
columns: [
{
field: "SoftwareUpLoad",
title: "#Resource.Field_SoftwareUpLoad",
template: kendo.template($("#template").html()),
// template: '#= SoftwareUpLoad==""?"<input type=\'file\' show=\'0\' name=\'files\' />":"<a class=\'k-button\' href=\'GetFiles?key=SoftwareUpLoad\'>Download</a>"#',
width: 300
},
{
field: "CutterCode",
title: "CutterCode",
width: 200
}
],
dataBound: function (e) {
var grid = this;
var firstItem = this.dataSource.view()[0];
this.tbody.find("input[name=files][show=0]").kendoUpload({
multiple: false,
async: {
saveUrl: 'save',
removeUrl: "remove",
autoUpload: true
},
validation: {
allowedExtensions: [".pdf"]
},
upload: function (e) {
var item = grid.dataItem(this.element.closest("tr"));
var id = BillId;
this.element.closest(".k-button").addClass("k-state-disabled");
this.element.closest(".k-button").find("input[name=files]").attr("show", "1");
e.data = { id: id, Operation: item.OperationNO };
},
remove: function (e) {
var item = grid.dataItem(this.element.closest("tr"));
var id = BillId;
this.element.closest(".k-button").removeClass("k-state-disabled");
e.data = { id: id, Operation: item.OperationNO };
},
success: function (e) {
var FileName = e.response.FileName;
var item = grid.dataItem(this.element.closest("tr"));
item.FileName = FileName;
item.dirty = false;
}
});
}
}).data("kendoGrid");
})
In the grid data line I upload data, click on the new line button, upload the data will be automatically emptied,but I want't that, I would like to ask how to do?

Related

VueJS Child component GMAP not rerender when parent data updates

Hi I'm new with Vue and I bump to this problem where when I update the location it doesn't reflect on the child component. I've used computed and watch but still not updating. forgive me as I don't have a strong knowledge about VueJS.
so in my code I have location(computed) which listen to localLocation(data) that is bind to the project.location. I update the location using the method setPlace()
hope anyone can help. here's my code below:
<template lang="pug">
.google-maps-wrapper
template(v-if="!location")
f7-list.no-margin-top
f7-list-button(title='Add Location' #click="isAutocompleteOpen = true")
template(v-if="location")
f7-list.no-margin(inline-labels no-hairlines-md)
f7-list-item.short-text Address: {{ location.formattedAddress }}
div
gmap-map.main-map(ref="googleMap" :options="mapOptions" :center="location.position" :zoom="16" :map-type-id="mapTypeId")
gmap-marker(:position="location.position" :clickable="false")
f7-actions(ref='mapsAction')
f7-actions-group
f7-actions-group
f7-actions-button(#click="copyToClipboard()") Copy to Clipboard
f7-actions-button(#click="isAutocompleteOpen = true") Change Address
f7-actions-button(v-if="$root.$device.ios || $root.$device.macos" #click="$refs.navigateActions.f7Actions.open()") Navigate
f7-actions-button(v-else #click="googleMapsNavigate()") Navigate
f7-actions-group
f7-actions-button
b Cancel
f7-actions(ref='navigateActions')
f7-actions-group
f7-actions-group
f7-actions-button(#click="googleMapsNavigate()") Google Maps
f7-actions-button(#click="appleMapsNavigation()") Apple Maps
f7-actions-group
f7-actions-button
b Cancel
f7-popup.locate-project(:opened='isAutocompleteOpen' #popup:closed='closeAutocomplete()')
f7-page
f7-navbar
f7-nav-title Search Location
f7-nav-right
f7-link(#click="closeAutocomplete()") Close
f7-searchbar.searchbar(search-container=".search-list" search-in=".item-title" #input="searchLocation($event)" placeholder="Enter Location" clear-button)
f7-list.not-found(v-if="!pendingSearch && !suggestedLocations.length && searchedLocation")
f7-list-item(title="Nothing found")
f7-block-title.gmap-preloader(v-if="pendingSearch")
f7-preloader(size="16")
f7-list.search-list.searchbar-found.gmap-search-list(v-if="!pendingSearch && suggestedLocations.length" media-list)
f7-list-item.item-button(v-for='(location, index) in suggestedLocations' :title="location.structured_formatting.main_text" :subtitle="location.structured_formatting.secondary_text" #click="updateLocation(location)")
</template>
<script>
import { debounce } from 'lodash-es'
import { Plugins } from '#capacitor/core'
const { Clipboard } = Plugins
const { Browser } = Plugins
const debounceSearch = debounce(run => {
run()
}, 500)
import defaultMixin from '#/mixins/default'
import {
f7Actions,
f7ActionsLabel,
f7ActionsGroup,
f7ActionsButton,
f7Popup,
f7Page,
f7NavRight,
f7NavTitle,
f7Navbar,
f7Block,
f7BlockTitle,
f7Label,
f7Link,
f7Preloader,
f7List,
f7ListButton,
f7ListItem,
f7ListInput,
f7Icon,
f7Searchbar
} from 'framework7-vue'
import { gmapApi } from 'vue2-google-maps'
export default {
name: "google-maps",
mixins: [defaultMixin],
props: ['project'],
components: {
f7Actions,
f7ActionsLabel,
f7ActionsGroup,
f7ActionsButton,
f7Popup,
f7Page,
f7NavRight,
f7NavTitle,
f7Navbar,
f7Block,
f7BlockTitle,
f7Label,
f7Link,
f7Preloader,
f7List,
f7ListButton,
f7ListItem,
f7ListInput,
f7Icon,
f7Searchbar
},
data() {
return {
mapTypeId: "terrain",
directionsService: undefined,
directionsDisplay: undefined,
autocompleteService: undefined,
autocompleteRequest: undefined,
navigate: false,
localLocation: this.project.location,
mapOptions: {
disableDefaultUI: true,
backgroundColor: '#d3d3d3',
draggable: false,
zoomControl: false,
fullscreenControl: false,
streetViewControl: false,
clickableIcons: false
},
isAutocompleteOpen: false,
suggestedLocations: [],
pendingSearch: false,
origin: '',
searchedLocation: ''
}
},
computed: {
location() {
return this.localLocation
},
google: gmapApi
},
methods: {
appleMapsNavigation(){
window.open(`http://maps.apple.com/?daddr=${encodeURI(this.project.location.formattedAddress)}`)
},
googleMapsNavigate(){
if(this.$root.$device.ios){
window.open(`comgooglemaps://?daddr=${encodeURI(this.project.location.formattedAddress)}`)
}else{
window.open(`https://www.google.com/maps/dir//${encodeURI(this.project.location.formattedAddress)}`)
}
},
closeAutocomplete() {
this.isAutocompleteOpen = false
this.searchedLocation = ''
this.suggestedLocations = []
this.$f7.searchbar.clear('.searchbar')
},
updateLocation( location ){
this.getGeocode(location.place_id, output => {
this.suggestedLocations = []
this.setPlace(output[0])
})
},
getGeocode( placeId, callback ){
const geocoder = new google.maps.Geocoder()
this.$f7.dialog.preloader()
geocoder.geocode({placeId}, output => {
callback(output)
this.closeAutocomplete()
this.$f7.dialog.close()
})
},
searchLocation( event ) {
this.pendingSearch = true
this.searchedLocation = event.target.value
debounceSearch(() => {
if(!this.searchedLocation) {
this.pendingSearch = false
this.suggestedLocations = []
return
}
const autocompleteService = new google.maps.places.AutocompleteService()
autocompleteService.getPlacePredictions({input: this.searchedLocation}, output => {
if(this.pendingSearch){
this.suggestedLocations = output || []
this.pendingSearch = false
}
})
})
},
setPlace( selectedLocation ) {
if(!selectedLocation.formatted_address) return;
const data = {
location: {
formattedAddress: selectedLocation.formatted_address,
position: {
lat: selectedLocation.geometry.location.lat(),
lng: selectedLocation.geometry.location.lng()
}
}
};
this.$f7.popup.close('.add-location')
if(this.$refs.autocomplete) this.$refs.autocomplete.$el.disabled = true
this.localLocation = data.location
db.collection("projects")
.doc(this.project.id)
.set(data, {
merge: true
})
.then()
},
copyToClipboard() {
Clipboard.write({
string: this.project.location.formattedAddress
});
}
}
}
</script>
Code Summary (just the summary of the whole code above)
Template that displays the address and map
.google-maps-wrapper
template(v-if="!location")
f7-list.no-margin-top
f7-list-button(title='Add Location' #click="isAutocompleteOpen = true")
template(v-if="location")
f7-list.no-margin(inline-labels no-hairlines-md)
f7-list-item.short-text Address: {{ location.formattedAddress }}
div
gmap-map.main-map(ref="googleMap" :options="mapOptions" :center="location.position" :zoom="16" :map-type-id="mapTypeId")
gmap-marker(:position="location.position" :clickable="false")
Last line of the template where it updates the location
f7-list-item.item-button(v-for='(location, index) in suggestedLocations' :title="location.structured_formatting.main_text" :subtitle="location.structured_formatting.secondary_text" #click="updateLocation(location)")
Script that updates the location
updateLocation( location ){
this.getGeocode(location.place_id, output => {
this.suggestedLocations = []
this.setPlace(output[0])
})
},
setPlace( selectedLocation ) {
if(!selectedLocation.formatted_address) return;
const data = {
location: {
formattedAddress: selectedLocation.formatted_address,
position: {
lat: selectedLocation.geometry.location.lat(),
lng: selectedLocation.geometry.location.lng()
}
}
};
this.$f7.popup.close('.add-location')
if(this.$refs.autocomplete) this.$refs.autocomplete.$el.disabled = true
this.localLocation = data.location
db.collection("projects")
.doc(this.project.id)
.set(data, {
merge: true
})
.then()
},
Initial Page No Address yet
Actual Output after adding location
Expected Output

How to use treeview with angular ui grid

Ho guys, i'm using angular ui grid in my project..heres my code :
$scope.gridOptions = {
enableSorting: false,
enableRowSelection: true,
multiSelect: false,
exporterMenuCsv: false,
// enableGridMenu: true,
enableSelectAll: true,
enableColumnResizing: true,
enableSorting: true,
onRegisterApi: function(gridApi) {
$scope.gridApi = gridApi; //set gridApi on scope
gridApi.core.on.renderingComplete($scope, function () {
$timeout(function () {
var gridBodyElem = document.getElementById(gridApi.grid.id + '-grid-container');
gridBodyElem.addEventListener('mouseup', handleGridClick);
});
});
},
columnDefs: [
{ field: 'name',displayName:'Name',cellClass: 'gridField'},
{ field: 'uniqueid',displayName:'ID',cellClass: 'gridField'},
{ field: 'devicetime',displayName:'GPS DATE',cellClass: 'gridField'},
{ field: 'adress',displayName:'Adresse',cellClass: 'gridField'},
{ field: 'company',displayName:'Company',cellClass: 'gridField'}
]
};
it works good but i want to use a treeview in this grid to obtain like this table grid below..
what i want exacly is, when i click on item, i want to show a html code below like the image above...is there anyone has any idea how to do this..??
Example controller :
var SomeController = function () {
this.customHtml = '<ul><li>render me please</li></ul>';
}
Example view :
<div ng:bind="customHtml"></div>
Gives :
<div>
"<ul><li>render me please</li></ul>"
</div>

JQGgrid- How to generate a pdf / excel file from the grid data

I have the following JQGrid table, which is being generated from JSON data. I want to be able to generate/export or create a pdf file based on the table data state that is view able. I am using JSON , Jqgrid and Javascript
How do I generate a pdf file from the data ?
Here My FIDDLE
JS CODE
$(document).ready(function() {
var jsonData = {
"Name": "Julie Brown",
"Account": "C0010",
"LoanApproved": "12/5/2015",
"LastActivity": "4/1/2016",
"PledgedPortfolio": "4012214.00875",
"MaxApprovedLoanAmt": "2050877.824375",
"LoanBalance": "1849000",
"AvailableCredit": "201877.824375",
"Aging": "3",
"Brokerage": "My Broker",
"Contact": "Robert L. Johnson",
"ContactPhone": "(212) 902-3614",
"RiskCategory": "Yellow",
"rows": [{
"ClientID": "C0010",
"Symbol": "WEC",
"Description": "Western Electric Co",
"ShareQuantity": "20638",
"SharePrice": "21.12",
"TotalValue": "435874.56",
"LTVCategory": "Equities",
"LTVRatio": "50%",
"MaxLoanAmt": "217937.28"
}, {
"ClientID": "C0010",
"Symbol": "BBB",
"Description": "Bins Breakers and Boxes",
"ShareQuantity": "9623",
"SharePrice": "74.29125",
"TotalValue": "714904.69875",
"LTVCategory": "Equities",
"LTVRatio": "50%",
"MaxLoanAmt": "357452.349375"
}, {
"ClientID": "C0010",
"Symbol": "GPSC",
"Description": "Great Plains Small Cap Stock",
"ShareQuantity": "49612",
"SharePrice": "14.24",
"TotalValue": "706474.88",
"LTVCategory": "Mutual Funds - Small Cap",
"LTVRatio": "40%",
"MaxLoanAmt": "282589.952"
}]
},
mmddyyyy = "";
/*********************************************************************/
$("#output").jqGrid({
url: "/echo/json/",
mtype: "POST",
datatype: "json",
postData: {
json: JSON.stringify(jsonData)
},
colModel: [
/** { name: 'ClientID', label:'ClientID',width: 80, key: true },****/
{
name: 'Symbol',
width: 65
}, {
name: 'Description',
width: 165
}, {
name: 'ShareQuantity',
align: 'right',
width: 85,
classes: "hidden-xs", labelClasses: "hidden-xs",
formatter: 'currency',
formatoptions: {
prefix: " ",
suffix: " "
}
}, {
name: 'SharePrice',
label: 'Share Price',
align: 'right',
width: 100,
classes: "hidden-xs", labelClasses: "hidden-xs",
template: "number",
formatoptions: {
prefix: " $",
decimalPlaces: 4
}
},
/*{ label: 'Value1',
name: 'Value1',
width: 80,
sorttype: 'number',
formatter: 'number',
align: 'right'
}, */
{
name: 'TotalValue',
label: 'Total Value',
width: 160,
sorttype: 'number',
align: "right",
search: false,
formatter: 'currency',
formatoptions: {
prefix: " $",
suffix: " "
}
}, {
name: 'LTVRatio',
label: 'LTV Ratio',
width: 70,
sorttype: 'number',
align: "right",
formatter: 'percentage',
formatoptions: {
prefix: " ",
suffix: " "
}
}, {
name: 'LTVCategory',
label: 'LTV Category',
classes: "hidden-xs", labelClasses: "hidden-xs",
width: 120,
width: 165
},
{
name: 'MaxLoanAmt',
label: 'MaxLoanAmount',
width: 165,
sorttype: 'number',
align: "right",
search: false,
formatter: 'currency',
formatoptions: {
prefix: " $",
suffix: " "
}
}
],
additionalProperties: ["Symbol", "Description"],
subGrid: true,
subGridRowExpanded: function (subgridDivId, rowid) {
var item = $(this).jqGrid("getLocalRow", rowid);
$("#" + $.jgrid.jqID(subgridDivId)).html("Symbol: <em>" + item.Symbol +
"</em><br/>Description: <em>" + item.Description + "</em>");
},
beforeProcessing: function (data) {
var symbolsMap = {}, symbolsValues = ":All", rows = data.rows, i, symbol;
for (i = 0; i < rows.length; i++) {
symbol = rows[i].Symbol;
if (!symbolsMap.hasOwnProperty(symbol)) {
symbolsMap[symbol] = 1;
symbolsValues += ";" + symbol + ":" + symbol;
}
}
$(this).jqGrid("setColProp", 'Symbol', {
stype: "select",
searchoptions: {
value: symbolsValues
}
}).jqGrid('destroyFilterToolbar')
.jqGrid('filterToolbar', {
stringResult: true,
searchOnEnter: false,
defaultSearch : "cn"
});
},
/*beforeProcessing: function (data) {
var item, i, n = data.length;
for (i = 0; i < n; i++) {
item = data[i];
item.Quantity = parseFloat($.trim(item.Quantity).replace(",", ""));
item.LTVRatio = parseFloat($.trim(item.LTVRatio *10000).replace(",", ""));
item.Value = parseFloat($.trim(item.Value).replace(",", ""));
item.Num1 = parseInt($.trim(item.Num1).replace(",", ""), 10);
item.Num2 = parseInt($.trim(item.Num2).replace(",", ""), 10);
}
}, */
iconSet: "fontAwesome",
loadonce: true,
rownumbers: true,
cmTemplate: {
autoResizable: true,
editable: true
},
autoResizing: {
compact: true
},
autowidth: true,
height: 'auto',
forceClientSorting: true,
sortname: "Symbol",
footerrow: true,
caption: "<b>Collateral Value</b> <span class='pull-right' style='margin-right:20px;'>Valuation as of: " + mmddyyyy + "</span>",
loadComplete: function() {
var $self = $(this),
sum = $self.jqGrid("getCol", "Price", false, "sum"),
sum1 = $self.jqGrid("getCol", "MaxLoanAmt", false, "sum");
//ltvratio = $self.jqGrid("getCol","LTVRatio:addas", "Aved Loan Amount");
$self.jqGrid("footerData", "set", {
LTVCategory: "Max Approved Loan Amount:",
Price: sum,
MaxLoanAmt: sum1
});
}
});
$("#output").jqGrid('filterToolbar', {stringResult: true, searchOnEnter: false, defaultSearch : "cn"});
$(window).on("resize", function () {
var newWidth = $("#output").closest(".ui-jqgrid").parent().width();
$("#output").jqGrid("setGridWidth", newWidth, true);
}).triggerHandle("resize");
});
There is no build in method to export jqgrid data to pdf. You have to use third party tools( I personally like iTextSharp which can be downloaded from Here . You have to create action method for printing the grid data in your controller.
Here is also another one example one guy made.
Here is also another example in trirand. If you see the source code they are using iTextHsarp.
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using JQGridMVCExamples.Models;
using Trirand.Web.Mvc;
using System.IO;
//
// For PDF export we are using the free open-source iTextSharp library.
//
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.Data;
namespace JQGridMVCExamples.Controllers.Grid
{
public partial class GridController : Controller
{
// This is the default action for the View. Use it to setup your grid Model.
public ActionResult ExportPDF()
{
// Get the model (setup) of the grid defined in the /Models folder.
var gridModel = new OrdersJqGridModel();
var ordersGrid = gridModel.OrdersGrid;
// Setting the DataUrl to an action (method) in the controller is required.
// This action will return the data needed by the grid
ordersGrid.DataUrl = Url.Action("PDFGrid_DataRequested");
// customize the default Orders grid model with custom settings
// NOTE: you need to call this method in the action that fetches the data as well,
// so that the models match
SetPDFExportGrid(ordersGrid);
// Pass the custmomized grid model to the View
return View(gridModel);
}
// This method is called when the grid requests data
public JsonResult PDFGrid_DataRequested()
{
// Get both the grid Model and the data Model
// The data model in our case is an autogenerated linq2sql database based on Northwind.
var gridModel = new OrdersJqGridModel();
var northWindModel = new NorthwindDataContext();
// customize the default Orders grid model with our custom settings
SetPDFExportGrid(gridModel.OrdersGrid);
// Save the current grid state in Session
// We will later need it for PDF Export
JQGridState gridState = gridModel.OrdersGrid.GetState();
Session["gridState"] = gridState;
// return the result of the DataBind method, passing the datasource as a parameter
// jqGrid for ASP.NET MVC automatically takes care of paging, sorting, filtering/searching, etc
return gridModel.OrdersGrid.DataBind(northWindModel.Orders);
}
public JsonResult PDFExport_AutoCompleteShipName(string term)
{
var northWindModel = new NorthwindDataContext();
JQAutoComplete autoComplete = new JQAutoComplete();
autoComplete.DataField = "ShipName";
autoComplete.AutoCompleteMode = AutoCompleteMode.BeginsWith;
autoComplete.DataSource = from o in northWindModel.Orders
select o;
return autoComplete.DataBind();
}
private void SetPDFExportGrid(JQGrid ordersGrid)
{
// show the search toolbar
ordersGrid.ToolBarSettings.ShowSearchToolBar = true;
ordersGrid.ToolBarSettings.ShowSearchButton = true;
var orderDateColumn = ordersGrid.Columns.Find(c => c.DataField == "OrderDate");
orderDateColumn.DataFormatString = "{0:yyyy/MM/dd}";
orderDateColumn.SearchType = SearchType.DatePicker;
orderDateColumn.DataType = typeof(DateTime);
orderDateColumn.SearchControlID = "DatePicker";
orderDateColumn.SearchToolBarOperation = SearchOperation.IsEqualTo;
var shipNameColumn = ordersGrid.Columns.Find(c => c.DataField == "ShipName");
shipNameColumn.SearchType = SearchType.AutoComplete;
shipNameColumn.DataType = typeof(string);
shipNameColumn.SearchControlID = "AutoComplete";
shipNameColumn.SearchToolBarOperation = SearchOperation.Contains;
var orderIDColumns = ordersGrid.Columns.Find(c => c.DataField == "OrderID");
orderIDColumns.Searchable = true;
orderIDColumns.DataType = typeof(int);
orderIDColumns.SearchToolBarOperation = SearchOperation.IsEqualTo;
SetPDFCustomerIDSearchDropDown(ordersGrid);
SetPDFFreightSearchDropDown(ordersGrid);
}
private void SetPDFCustomerIDSearchDropDown(JQGrid ordersGrid)
{
// setup the grid search criteria for the columns
JQGridColumn customersColumn = ordersGrid.Columns.Find(c => c.DataField == "CustomerID");
customersColumn.Searchable = true;
// DataType must be set in order to use searching
customersColumn.DataType = typeof(string);
customersColumn.SearchToolBarOperation = SearchOperation.IsEqualTo;
customersColumn.SearchType = SearchType.DropDown;
// Populate the search dropdown only on initial request, in order to optimize performance
if (ordersGrid.AjaxCallBackMode == AjaxCallBackMode.RequestData)
{
var northWindModel = new NorthwindDataContext();
var searchList = from customers in northWindModel.Customers
select new SelectListItem
{
Text = customers.CustomerID,
Value = customers.CustomerID
};
customersColumn.SearchList = searchList.ToList<SelectListItem>();
customersColumn.SearchList.Insert(0, new SelectListItem { Text = "All", Value = "" });
}
}
private void SetPDFFreightSearchDropDown(JQGrid ordersGrid)
{
// setup the grid search criteria for the columns
JQGridColumn freightColumn = ordersGrid.Columns.Find(c => c.DataField == "Freight");
freightColumn.Searchable = true;
// DataType must be set in order to use searching
freightColumn.DataType = typeof(decimal);
freightColumn.SearchToolBarOperation = SearchOperation.IsGreaterOrEqualTo;
freightColumn.SearchType = SearchType.DropDown;
// Populate the search dropdown only on initial request, in order to optimize performance
if (ordersGrid.AjaxCallBackMode == AjaxCallBackMode.RequestData)
{
List<SelectListItem> searchList = new List<SelectListItem>();
searchList.Add(new SelectListItem { Text = "> 10", Value = "10" });
searchList.Add(new SelectListItem { Text = "> 30", Value = "30" });
searchList.Add(new SelectListItem { Text = "> 50", Value = "50" });
searchList.Add(new SelectListItem { Text = "> 100", Value = "100" });
freightColumn.SearchList = searchList.ToList<SelectListItem>();
freightColumn.SearchList.Insert(0, new SelectListItem { Text = "All", Value = "" });
}
}
public ActionResult ExportToPDF(string exportType)
{
var gridModel = new OrdersJqGridModel();
var northWindModel = new NorthwindDataContext();
var grid = gridModel.OrdersGrid;
// Get the last grid state the we saved before in Session in the DataRequested action
JQGridState gridState = Session["GridState"] as JQGridState;
// Need to set grid options again
SetExportGrid(grid);
if (String.IsNullOrEmpty(exportType))
exportType = "1";
DataTable exportData;
switch (exportType)
{
case "1":
grid.ExportSettings.ExportDataRange = ExportDataRange.All;
exportData = grid.GetExportData(northWindModel.Orders);
ExportToPDF(exportData);
break;
case "2":
grid.ExportSettings.ExportDataRange = ExportDataRange.Filtered;
exportData = grid.GetExportData(northWindModel.Orders, gridState);
ExportToPDF(exportData);
break;
case "3":
grid.ExportSettings.ExportDataRange = ExportDataRange.FilteredAndPaged;
exportData = grid.GetExportData(northWindModel.Orders, gridState);
ExportToPDF(exportData);
break;
}
return View();
}
private void ExportToPDF(DataTable dt)
{
//
// For PDF export we are using the free open-source iTextSharp library.
//
Document pdfDoc = new Document();
MemoryStream pdfStream = new MemoryStream();
PdfWriter pdfWriter = PdfWriter.GetInstance(pdfDoc, pdfStream);
pdfDoc.Open();//Open Document to write
pdfDoc.NewPage();
Font font8 = FontFactory.GetFont("ARIAL", 7);
PdfPTable PdfTable = new PdfPTable(dt.Columns.Count);
PdfPCell PdfPCell = null;
//Add Header of the pdf table
for (int column = 0; column < dt.Columns.Count; column++)
{
PdfPCell = new PdfPCell(new Phrase(new Chunk(dt.Columns[column].Caption, font8)));
PdfTable.AddCell(PdfPCell);
}
//How add the data from datatable to pdf table
for (int rows = 0; rows < dt.Rows.Count; rows++)
{
for (int column = 0; column < dt.Columns.Count; column++)
{
PdfPCell = new PdfPCell(new Phrase(new Chunk(dt.Rows[rows][column].ToString(), font8)));
PdfTable.AddCell(PdfPCell);
}
}
PdfTable.SpacingBefore = 15f; // Give some space after the text or it may overlap the table
pdfDoc.Add(PdfTable); // add pdf table to the document
pdfDoc.Close();
pdfWriter.Close();
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AppendHeader("Content-Disposition", "attachment; filename=gridexport.pdf");
Response.BinaryWrite(pdfStream.ToArray());
Response.End();
}
}
}

Kendo UI - JSON Response for - Grid using Remote Data Source W/Server Grouping & Server Aggregates

I have a project in which I'm using the KendoUI Grid using the server to get the data instead of locally.
I'm not sure what the JSON response should be from my server to get grouping to work however. My goal is when the user drags a column to the grouping header that I know what kind of JSON response to give back so the GRID groups by that column and any other columns that might be added to that header.
Given the image above, how do I create a JSON response to fulfill it (so its showing what its supposed to grouped)? I get I have to do this on my own on the server but not sure how JSON needs to be formated. Furthermore if I want to show a 'count' field next to the groups when they are created so I know how many items are in each group (which I believe is the aggregate?)
My current grid codes looks like the following:
<div id="grid" style="height:100%;"></div>
<script>
$(window).on("resize", function() {
kendo.resize($("#grid"));
});
var crudServiceBaseUrl = "/api",
dataSource = new kendo.data.DataSource({
transport: {
read: {
url: crudServiceBaseUrl + "/companies",
dataType: "json",
type: "POST"
},
update: {
url: crudServiceBaseUrl + "/companies/update",
dataType: "json",
type: "POST"
},
destroy: {
url: crudServiceBaseUrl + "/companies/destroy",
dataType: "json",
type: "POST"
},
create: {
url: crudServiceBaseUrl + "/companies/create",
dataType: "json",
type: "POST"
},
parameterMap: function(options, operation) {
if (operation !== "read" && options.models) {
return {models: kendo.stringify(options.models)};
}
}
},
error: function (e) {
/* the e event argument will represent the following object:
{
errorThrown: "custom error",
errors: ["foo", "bar"]
sender: {... the Kendo UI DataSource instance ...}
status: "customerror"
xhr: null
}
*/
//alert("Status: " + e.status + "; Error message: " + e.errorThrown);
console.log("Status: " + e.status + "; Error message: " + e.errorThrown);
console.log("Errors: " + e.errors.join("; "));
},
autoSync: false,
serverPaging: true,
serverFiltering: true,
serverSorting: true,
serverGrouping: true,
serverAggregates: true,
pageSize: 20,
columnResizeHandleWidth: 6,
schema: {
total: "itemCount",
data: "items",
groups: "groups",
aggregates: "aggregates",
group: {
field: "phone", aggregates: [{ field: "phone", aggregate: "count" }]
},
model: {
id: "id",
fields: {
id: { editable: false, nullable: true },
name: { validation: { required: true } },
phone: {
type: "string",
validation: {
required: true,
phonerule: function(e){
if (e.is("[data-phonerule-msg]"))
{
var input = e.data('kendoMaskedTextBox');
//If we reached the end of input then it will return -1 which means true, validation passed
//Otherwise it won't === -1 and return false meaning all the characters were not entered.
return input.value().indexOf(input.options.promptChar) === -1;
}
return true; //return true for anything else that is not data-phonerule-msg
}
}
},
email: { type: "string", validation: { required: true, email:true } }
}
}
}
});
$("#grid").kendoGrid({
dataSource: dataSource,
groupable: true,
sortable: {
mode: "multiple",
allowUnsort: true
},
selectable: "multiple cell",
allowCopy:true,
toolbar: ["create","excel"],
excel: {
fileName: "Kendo UI Grid Export.xlsx",
//Below is only used as fallback for old browsers without support
proxyURL: "//demos.telerik.com/kendo-ui/service/export",
filterable: true
},
pageable: {
refresh: true,
pageSizes: true,
buttonCount: 5
},
reorderable: true,
resizable: true,
columnMenu: true,
filterable: true,
editable: "popup",
mobile: true,
columns: [
{
field: "name",
title: "Company Name",
aggregates: ["count"],
groupFooterTemplate: "Count: #=count#"
},
{
field: "phone",
title: "Phone",
editor: function(container, options){
//pattern="[(][0-9]{3}[)] [0-9]{3}-[0-9]{4}"
var input = $('<input type="tel" data-phonerule-msg="Invalid Phone Number!" class="k-textbox" required />');
input.attr("name", options.field);
input.kendoMaskedTextBox({
mask: "(999) 000-0000"
});
input.appendTo(container);
},
aggregates: ["count"],
groupFooterTemplate: "Count: #=count#"
},
{
field: "email",
title: "Email",
editor: function(container, options){
var input = $('<input type="email" data-email-msg="Invalid email!" class="k-textbox" required/>');
input.attr("name", options.field);
input.appendTo(container);
},
aggregates: ["count"],
groupFooterTemplate: "Count: #=count#"
},
{
command: ["edit", "destroy"],
title: "Operations",
width: "240px"
}
],
});
</script>
My current code that generates the demo javascript via Symfony 3.0 is below in..
DefaultController.php
/**
* #Route("/api/companies", name="api_companies_read")
*/
public function apiCompaniesReadAction(Request $request)
{
$data["itemCount"] = "7";
// $tdata["field"] = "";
// $tdata["value"] = "";
// $tdata["items"] = "hey";
// $data["groups"][] = $tdata;
$tdata["id"] = "1";
$tdata["name"] = "Joe";
$tdata["phone"] = "(714)475-8651";
$tdata["email"] = "Joe#whatever.com";
$data["items"][] = $tdata;
$tdata["id"] = "2";
$tdata["name"] = "Rachel";
$tdata["phone"] = "(563)812-4184";
$tdata["email"] = "rachel#yahoo.com";
$data["items"][] = $tdata;
$tdata["id"] = "3";
$tdata["name"] = "John";
$tdata["phone"] = "(563)812-4184";
$tdata["email"] = "John#yahoo.com";
$data["items"][] = $tdata;
$tdata["id"] = "4";
$tdata["name"] = "Richard";
$tdata["phone"] = "(563)812-4184";
$tdata["email"] = "John#yahoo.com";
$data["items"][] = $tdata;
$tdata["id"] = "5";
$tdata["name"] = "Sister";
$tdata["phone"] = "(563)812-4184";
$tdata["email"] = "John#yahoo.com";
$data["items"][] = $tdata;
$tdata["id"] = "6";
$tdata["name"] = "Brother";
$tdata["phone"] = "(563)812-4184";
$tdata["email"] = "Brother#yahoo.com";
$data["items"][] = $tdata;
$tdata["id"] = "7";
$tdata["name"] = "Sibling";
$tdata["phone"] = "(563)812-4184";
$tdata["email"] = "Sibling#yahoo.com";
$data["items"][] = $tdata;
// schema: {
// total: "total",
// model: {
// id: "CompanyID",
// fields: {
// CompanyID: { editable: false, nullable: true },
// Name: { validation: { required: true } },
// Phone: { type: "string" },
// Email: { type: "string" }
// }
// }
// }
// replace this example code with whatever you need
return new JsonResponse($data);
}
The current JSON it creates looks like this.
JSON
{"itemCount":"7","items":[{"id":"1","name":"Joe","phone":"(714)475-8651","email":"Joe#whatever.com"},{"id":"2","name":"Rachel","phone":"(563)812-4184","email":"rachel#yahoo.com"},{"id":"3","name":"John","phone":"(563)812-4184","email":"John#yahoo.com"},{"id":"4","name":"Richard","phone":"(563)812-4184","email":"Richard#yahoo.com"},{"id":"5","name":"Sister","phone":"(563)812-4184","email":"Sister#yahoo.com"},{"id":"6","name":"Brother","phone":"(563)812-4184","email":"Brother#yahoo.com"},{"id":"7","name":"Sibling","phone":"(563)812-4184","email":"Sibling#yahoo.com"}]}
I found an article here that looks close to what I want just hard to understand. This might also help.
I created JSFiddle that can be played around with, just needs to be supplied valid data.
Update
I got server paging to work! The first major change is on the transport for the datasource you have to change the parameterMap to send json so you can access what its trying to tell your server to change.
dataSource = new kendo.data.DataSource({
transport: {
read: {
url: crudServiceBaseUrl + "/companies",
dataType: "json",
type: "POST"
},
update: {
url: crudServiceBaseUrl + "/companies/update",
dataType: "json",
type: "POST"
},
destroy: {
url: crudServiceBaseUrl + "/companies/destroy",
dataType: "json",
type: "POST"
},
create: {
url: crudServiceBaseUrl + "/companies/create",
dataType: "json",
type: "POST"
},
parameterMap: function(options, operation) {
return kendo.stringify(options);
}
},
My datasource looks like above, but you could adjust to your needs. Next my php file below.
PHP
/companies
/**
* #Route("/api/companies", name="api_companies_read")
*/
public function apiCompaniesReadAction(Request $request)
{
$request_body = file_get_contents('php://input');
$json = json_decode($request_body);
//Based on the JSON Payload response adjust the search in the database
if($json){
$page = $json->page;
$pageSize = $json->pageSize;
$skip = $json->skip;
$take = $json->take;
}else{
$page = 1;
$pageSize = 20;
$skip = 1;
$take = 1;
}
$repository = $this->getDoctrine()->getRepository('AppBundle:Company');
/*
findBy(
array $criteria,
array $orderBy = null,
integer|null $limit = null,
integer|null $offset = null
)
*/
$company_total = $repository->findAll();
$company_records = $repository->findBy(array(),array(),$pageSize,($page-1)*$pageSize);
$data["total"] = count($company_total);
foreach($company_records as $company){
$temp["id"] = $company->getId();
$temp["name"] = $company->getName();
$temp["phone"] = $company->getPhone();
$temp["email"] = $company->getEmail();
$data["data"][] = $temp;
}
//converts data to JSON
return new JsonResponse($data);
}
Please remember the above is a Symfony 3.0 php implementation using annotations to set the route. The important part in that code is.
$request_body = file_get_contents('php://input');
$json = json_decode($request_body);
//Based on the JSON Payload response adjust the search in the database
if($json){
$page = $json->page;
$pageSize = $json->pageSize;
$skip = $json->skip;
$take = $json->take;
}else{
$page = 1;
$pageSize = 20;
$skip = 1;
$take = 1;
}
The file_get_contents('php://input'); gets the javascript object that the KendoUI control sends back.
UPDATE # 3 Got Server Sorting Working!
Below is an example implementaiton using Symfony 3.0 and Doctrine to do Server Sorting!
PHP
/**
* #Route("/api/companies", name="api_companies_read")
*/
public function apiCompaniesReadAction(Request $request)
{
$request_body = file_get_contents('php://input');
$json = json_decode($request_body);
//print_r($json);
//default parameters in case of error.
$page = 1;
$pageSize = 20;
$skip = 1;
$take = 1;
$sort = array();
//Based on the JSON Payload response adjust the search in the database
if(isset($json)){
$page = $json->page;
$pageSize = $json->pageSize;
$skip = $json->skip;
$take = $json->take;
if(isset($json->sort)){
//"sort":[{"field":"name","dir":"asc"}]}:
foreach($json->sort as $sortObj){
$sort[$sortObj->field] = $sortObj->dir;
}
}
}
$repository = $this->getDoctrine()->getRepository('AppBundle:Company');
/*
findBy(
array $criteria,
array $orderBy = null,
integer|null $limit = null,
integer|null $offset = null
)
*/
$company_total = $repository->findAll();
$company_records = $repository->findBy(array(),$sort,$pageSize,($page-1)*$pageSize);
$data["total"] = count($company_total);
foreach($company_records as $company){
$temp["id"] = $company->getId();
$temp["name"] = $company->getName();
$temp["phone"] = $company->getPhone();
$temp["email"] = $company->getEmail();
$data["data"][] = $temp;
}
//converts data to JSON
return new JsonResponse($data);
}
That first link is spot on. I had to export data from a grid to pdf and excel adhering to the grid grouping. I finally figured out the semantics. The grid has columns and data objects which you can get through grid calls. The data object holds the grouping information. Here is the processing operation.
In a nutshell, you have to recursively iterate the data object tpo determine the grouping. For each element in the data you need to inspect if the data[x].value exists. If it exists then it is a group object with child data. If it does not exists then you use the normal data[row][column].field to get to the child data. The trick here is to call a recurisive function for each point where dat[0].value has data and finally process the data[row][column] on the unwind.
Below is a js function you can use to inspect the Grid's json dta. I would add a group or two to your grid and see the difference between the data with/without grouping applied and that should be what you need to add to your data....I think you pretty much add data as data={[[],[]]}unless it is a group, then it is data={[value='groupName',{[],[]}]}.
function showData(gridName) {
var grid = $('#' + gridName).data('kendoGrid');
var data = grid.dataSource.data().toJSON();
console.log(data);
}

Paging with jsonRest in Ehancedgrid in dojo

i have 100000 records in database...i have to display 50 at a time in enhanced grid when i scroll down next request goes to server and get next 50 like wise ......
i came across that this can be achieved by jsonRestStore....i tried with that but i am not getting that..how to use jsonRest for this purpose ????..plz sugggest me answer
my code is
require([
"dojo/_base/lang", "dojox/grid/EnhancedGrid",
"dojox/grid/enhanced/plugins/Search","dojox/grid/enhanced/plugins/Filter",
"dojox/grid/enhanced/plugins/Pagination","dojo/data/ItemFileWriteStore",
"dojo/store/JsonRest","dijit/form/Button","dojo/request/xhr", "dojo/dom",
"dojo/dom-construct", "dojo/json", "dojo/on", "dojox/grid/cells/dijit",
"dojo/domReady!"
], function(lang,EnhancedGrid,Search,Filter,Pagination,ItemFileWriteStore,JsonRest,Button,xhr, dom, domConst, JSON, on) {
xhr("//myipaddress/GridExample/string", {
handleAs : "json"
}).then(function(dataa){
/* domConst.place("<p>response: <code>" + JSON.stringify(dataa) + "</code></p>", "output"); */
/* domConst.place("<p>response: <code>" + JSON.stringify(dataa) + "</code></p>", "output"); */
var mydata=dataa;
var yourStore = new dojo.data.ItemFileWriteStore({
data: {
identifier: "sno",
/* items: mydata.aa */
items:mydata
}
});
grid = new EnhancedGrid({
id:'grid',
store : yourStore,
structure : layout,
rowSelector: '20px',
plugins: {
search:true,
pagination: {
pageSizes: ["50","100","500","1000"],
description: true,
sizeSwitch: true,
pageStepper: true,
gotoButton: true,
maxPageStep: 2,
position: "bottom"
},
filter: {
closeFilterbarButton: true,
ruleCount: 5,
itemsName: "rows"
}
}
});
grid.placeAt("myGrid");
grid.startup();
}, function(err) {
alert("error");
}, function(evt) {
});
var id=100000;
var addbutton = new Button({
onClick: function (){
id++;
/* alert(dojox.grid.scroller.firstVisibleRow);
alert(dojox.grid.scroller.lastVisibleRow); */
console.log(arguments);
grid.store.newItem({
sno:id,
sname:'san',
salary:'25000'
});
store.save();
grid.render();
}
}, "addRow");
var removebutton = new Button({
onClick: function (){
var items = grid.selection.getSelected();
alert(items);
if(items.length){
dojo.forEach(items, function(selectedItem){
if(selectedItem !== null){
store.deleteItem(selectedItem);
store.save();
}
});
}
grid.render();
}
}, "removeRow");
var updatebutton = new Button({
onClick: function (){
}
}, "updateRow");
var store1 = new JsonRest({
target: "http://localhost:7080/GridExample/string"
});
store.get(3).then(function(item){
alert(item);
});
});
I am sure you must have found answer to your question by now. But to help others, You haven't tied your JsonRest store (store1) to grid.
Here is example code that does this:
//jsonstore
var jsonStore = new dojo.store.Cache(new dojo.store.JsonRest({
target : "http://localhost:7080/GridExample/string",
idProperty : "id"
}), dojo.store.Memory({
idProperty : "id"
}));
grid = new dojox.grid.EnhancedGrid({
store : dataStore = dojo.data.ObjectStore({
objectStore : jsonStore
}),
id: "grid_id",
structure : layout,
rowSelector : '20px',
selectionMode : "single",
clientSort : true,
plugins : {
selector : true,
/**nestedSorting : true,**/
pagination : {
defaultPageSize : 20,
pageSizes : [ 20, 50, 100, "All" ],
description : true, // display the current position
sizeSwitch : true, // display the page length menu
pageStepper : true, // display the page navigation choices
gotoButton : true, // go to page button
/*page step to be displayed*/
maxPageStep : 4,
/*position of the pagination bar*/
position : "bottom"
}
}
}, "search_results_grid"); // make sure you have a target HTML element with this id