Kendo Grid Fluent API DataBound event for a Child Grid to show No Items text? - kendo-grid

It feels like i've lost a lot of time looking for this and still haven't found anything that works (well, that works properly). I have a set of nested grids for a user requirement, each one drills down into the next one etc and all that is working fine. It's handled using client templates which do an ajax call when they get expanded, and then displays the data.
The problem I have is that if there are no results for one of the expansions, Kendo just shows the child grid header and nothing else. When I connect to the DataBound event (on the grid, .Events(e => e.DataBound("myJavaScriptFunctionName")) the this is not the Kendo Grid, and if I pass the name of the child kendo grid (which is unique by using the key in #=#) it gives me 0 items in my data source.
I'm not sure if this is because it does an ajax post back, and then OnDataBound fires before it comes back?
I just need to show a "No Items Found" message to make the user experience better when there is no data (this really only happens at the lowest level)
Enough with words, here is some example code:
<script id="SecondToLastTemplate" type="text/kendo-tmpl">
#(Html.Kendo().Grid<CustomerViewModel>()
.Name("SumGrid_#=ResultYear#_#=ResultQuarter#_#=ResultMonth#_#=ResultWeekStart#_#=ResultDate#_#=Region#")
.Columns(column =>
{
column.Bound(x => x.CustomerName).Width("23%");
column.Bound(x => x.CustomerSummaryItem1).Width("14%");
column.Bound(x => x.CustomerSummaryItem2).Width("14%");
column.Bound(x => x.CustomerSummaryItem3).Width("14%");
})
.DataSource(dataBinding => dataBinding
.Ajax()
.PageSize(500)
.Read(read => read.Action("GetCustomerSummaryItems", Constants.Controller_ReportController, new
{
ResultYear = "#=ResultYear#"
,ResultQuarter = "#=ResultQuarter#"
,ResultMonth = "#=ResultMonth#"
,ResultWeekStart = "#=ResultWeekStart#"
,ResultDate = "#=ResultDate#"
,Region = "#=Region#"
}))
)
.Scrollable(scrolling => scrolling.Enabled(false))
.Sortable()
.Filterable(filtering => filtering.Enabled(true))
.ClientDetailTemplateId("LastTemplate")
.Pageable(paging => paging.Enabled(false))
.ToClientTemplate()
)
</script>
<script id="LastTemplate" type="text/kendo-tmpl">
#(Html.Kendo().Grid<CustomerDetailsViewModel>()
.Name("SumGrid_#=ResultYear#_#=ResultQuarter#_#=ResultMonth#_#=ResultWeekStart#_#=ResultDate#_#=Region#_#=CustomerName#")
.Columns(column =>
{
column.Bound(x => x.CustomerDetails1).Width("23%");
column.Bound(x => x.CustomerDetails2).Width("20%");
column.Bound(x => x.CustomerDetails3).Width("35%");
column.Bound(x => x.CustomerDetails4).Width("14%");
})
.DataSource(dataBinding => dataBinding
.Ajax()
.PageSize(500)
.Read(read => read.Action("GetCustomerDetails", Constants.Controller_ReportController, new
{
ResultYear = "#=ResultYear#"
,ResultQuarter = "#=ResultQuarter#"
,ResultMonth = "#=ResultMonth#"
,ResultWeekStart = "#=ResultWeekStart#"
,ResultDate = "#=ResultDate#"
,Region = "#=Region#"
,CustomerName = "#=CustomerName#"
}))
)
.Events(e => e.DataBound("onDataBound"))
.Scrollable(scrolling => scrolling.Enabled(false))
.Sortable()
.Filterable(filtering => filtering.Enabled(true))
.Pageable(paging => paging.Enabled(false))
.ToClientTemplate()
)
</script>
OnDataBound i've tried a few things, including the answer from this thread (Display a message within the Kendo grid when it's empty) with no luck. That one specifically always tells me I have 0 items in my data source (originally it was undefined, then I passed the grid name and still no luck).
Does anyone have a nice way to just say "No Items to display" while using the Fluent API with nested grids? I feel like i'm missing something simple here.
Thank you!

I figured these out:
Since I was using an ajax post back, the grid items weren't always available when the DataBound event was being called for some reason (it feels like they should be, since it's DataBound, but it wasn't)
I wrapped my no-results query in a setTimeout of 500ms so as to catch it, anything less and I would go back to the original error. I also modified the .find(... call to remove k-grid-header since that class wasn't outputting on my grid, and the colgroup was directly under the k-grid on the table.
function DisplayNoResultsFound(e) {
var self = e;
setTimeout(function (item) {
var grid = self;
var dataSource = grid.data('kendoGrid').dataSource;
var colCount = grid.find('colgroup > col').length;
var noResultsMessage = '#Resources.Global.NoResultsFound';
// If there are no results place an indicator row
if (dataSource._view.length == 0) {
grid.find('tbody')
.append('<tr class="kendo-data-row"><td colspan="' + colCount + '" style="text-align:center"><b>' + noResultsMessage + '</b></td></tr>');
}
}, 500); //Need to delay for ajax postback
}
This code is called by passing the jQuery grid item from the Databound Event:
.Events(e => e.DataBound(DisplayNoResultsFound($('\\#SumGrid_#=ResultYear#_#=ResultQuarter#_#=ResultMonth#_#=ResultWeekStart#_#=ResultDate#_#=Region#_#=CustomerName#'))")
Hope this helps someone else in the future!

Related

Conditionally make a page read-only using react

I want to create a React webpage that has both editable and read-only versions, the whole page not just a few elements on the page. A version is displayed to the user based on user id and other conditions. How do I do it?
The only straight forward way I know is to create 2 pages one editable and one read-only and based on the condition show the appropriate version (html page) to the user.
Is there a better and smarter way to do this? Like can I create just one page for both versions and toggle the mode based on the condition to the users?
Your question should have provided an example of some code you had tried but based on the description, very rough example below of one of many possible solutions.
Suppose EditView component is your page and you are able to pass a value for permission based on whatever credential you need to apply.
Then you have a component, ExampleField that takes the permission and displays either an input or static text. A collection of multiple of these fields is mapped from a theoretical array of data that you'll have to fetch from somewhere and the fields are returned by the main component.
const EditView = ({permission}) => {
const [editable, setEditable] = useState();
const [values, setValues] = useState([]);
useEffect(() => {
setEditable(permission);
}, [permission]);
useEffect(() => {
//maybe fetch your data from a back end or whatever and assign it to `values`
//on page load
}, [])
const ExampleField = ({permission, val, index}) => {
const handleChange = (e) => {
let vals = [...values];
vals[index] = val;
setValues(vals);
}
return(
<>
{permission
? <input name="example" type="text" defaultValue={val}
onChange={handleChange} />
: <span>{val}</span>}
</>
)
}
const fields = values.map((value, i) => {
return <ExampleField permission={permission} val={value} index={i}/>
})
return(
<>
{fields}
</>
)
}
Most likely, you'll want to break out various field components into their own file and, instead of using useState, you would probably want to explore useContext or useStore type functionality to lift up your state and do all the react things.
*Haven't tested or even compiled this code - for illustration purposes only.

How to get kendo grid dropdownchange event for MVC

I have used MVC kendo grid and I have bind the dropdown to grid. Now I have to get the dropdownchange event to populate other grid items by using dropdown selection.
columns.ForeignKey(c => c.CountryID, (SelectList)ViewBag.Countries).Title("Select Country");
$("#ddlTables").change(function () {
//You will get change event here
//Add debugger here and see
//Do your code here
});
columns.ForeignKey(c => c.CountryID, (SelectList)ViewBag.Countries,new {#id = "ddlCountry"}).Title("Select Country");
Here is the code replace this with your code and try to do your stuff and if still facing issue let me know
You can do it using an editor template as follows.
change the column as follows
columns.Bound(c => c.CountryID).Title("Country").EditorTemplateName("Countries").Width(300);
after that create a partial view inside views/shared/EditorTemplates with name Countries as follows
#using System.Collections
#(Html.Kendo().DropDownList()
.DataValueField("COUNTRYNAME")
.DataTextField("COUNTRYNAME")
.Name("CountryID")
.BindTo((IEnumerable)ViewBag.Countries)
.OptionLabel("Select Country")
.Filter(FilterType.Contains)
.Events(e =>
{
e.Change("CountryChange");
})
)
After this you can write jquery as follows
<script>
function CountryChange()
{
//You will get change event here
}

Handling events from a Kendo MVC Grid's PopUp editor window

I have a Kendo MVC grid that I am creating with the Html.Kendo().Grid helper. When the PopUp editor window opens, I want to catch the event and run a bit of javascript. When I configure a normal kendo window with .Events, the events fire properly and my function runs. However, when I code the .Events property on the .Editable.Window of the grid, the events do not fire.
#(Html.Kendo().Grid<FooRecord>()
.Name("cFooGrid")
.Columns(c =>
{
c.Bound(f => f.Foo);
c.Bound(f => f.Bar);
c.Bound(f => f.Bas);
c.Command(a => a.Edit());
})
.Editable(e => e
.Mode(GridEditMode.PopUp)
.Window(w => w.Events(v => v.Open("OnEditStart").Activate(#<text>function () {console.log("EditWindow.Activate")}</text>)))
)
.ToolBar(t =>
{
t.Create();
})
.DataSource(ds => ds
.Ajax()
.Create(r => r.Action("UpdateIndex", "Home"))
.Read(r => r.Action("IndexList", "Home"))
.Update(u => u.Action("UpdateIndex", "Home"))
.Model( m => {
m.Id(f => f.Foo);
})
)
)
When I review the generated code in Chrome's developer tools, the window is generated without the Activate or Open features:
jQuery(function(){jQuery("#cFooGrid").kendoGrid({"columns":[{"title":"Foo Key","field":"Foo","encoded":true,"editor":null},{"title":"Bar Field","field":"Bar","encoded":true,"editor":null},{"title":"Bas Value","field":"Bas","encoded":true,"editor":null},{"command":[{"name":"edit","buttonType":"ImageAndText","text":"Edit"}]}],"scrollable":false,"editable":{"confirmation":"Are you sure you want to delete this record?","confirmDelete":"Delete","cancelDelete":"Cancel","mode":"popup","template":"\u003cdiv class=\"editor-label\"\u003e\u003clabel for=\"Foo\"\u003eFoo Key\u003c/label\u003e\u003c/div\u003e\u003cdiv class=\"editor-field\"\u003e\u003cinput class=\"k-textbox\" id=\"Foo\" name=\"Foo\" /\u003e\u003cspan class=\"field-validation-valid\" data-valmsg-for=\"Foo\" data-valmsg-replace=\"true\"\u003e\u003c/span\u003e\u003c/div\u003e\u003cdiv class=\"editor-label\"\u003e\u003clabel for=\"Bar\"\u003eBar Field\u003c/label\u003e\u003c/div\u003e\u003cdiv class=\"editor-field\"\u003e\u003cinput class=\"k-textbox\" data-val=\"true\" data-val-maxlength=\"The field Bar Field must be a string or array type with a maximum length of \u0026\\#39;20\u0026\\#39;.\" data-val-maxlength-max=\"20\" id=\"Bar\" name=\"Bar\" /\u003e\u003cspan class=\"field-validation-valid\" data-valmsg-for=\"Bar\" data-valmsg-replace=\"true\"\u003e\u003c/span\u003e\u003c/div\u003e\u003cdiv class=\"editor-label\"\u003e\u003clabel for=\"Bas\"\u003eBas Value\u003c/label\u003e\u003c/div\u003e\u003cdiv class=\"editor-field\"\u003e\u003cinput class=\"k-textbox\" data-val=\"true\" data-val-required=\"The Bas Value field is required.\" id=\"Bas\" name=\"Bas\" /\u003e\u003cspan class=\"field-validation-valid\" data-valmsg-for=\"Bas\" data-valmsg-replace=\"true\"\u003e\u003c/span\u003e\u003c/div\u003e","window":{"title":"Edit","modal":true,"draggable":true,"resizable":false},"create":true,"update":true,"destroy":true},"toolbar":{"command":[{"name":null,"buttonType":"ImageAndText","text":"Add new record"}]},"dataSource":{"type":(function(){if(kendo.data.transports['aspnetmvc-ajax']){return 'aspnetmvc-ajax';} else{throw new Error('The kendo.aspnetmvc.min.js script is not included.');}})(),"transport":{"read":{"url":"/Home/IndexList"},"prefix":"","update":{"url":"/Home/UpdateIndex"},"create":{"url":"/Home/UpdateIndex"}},"serverPaging":true,"serverSorting":true,"serverFiltering":true,"serverGrouping":true,"serverAggregates":true,"filter":[],"schema":{"data":"Data","total":"Total","errors":"Errors","model":{"id":"Foo","fields":{"Foo":{"type":"string"},"Bar":{"type":"string"},"Bas":{"type":"string"}}}}}});});
Or, more specifically:
"window":{"title":"Edit","modal":true,"draggable":true,"resizable":false}
I would expect that the window would be generated with Activate: and Open: parameters, but they don't show up. Can anyone give me a pointer as to whether this just isn't supported or I am doing something wrong?
Edit:
So in order to capture the events as above, there are two steps:
Add this to the grid definition (remove the Window .Events)
.Events(e => e.Edit("OnEditStart"))
Then add a javascript function like this to the page.
function OnEditStart(pEvent) {
var editWindow = pEvent.container.data('kendoWindow');
editWindow.bind('activate', function () {
console.log('Edit start event fired');
});
}
NOTE: There does not appear to be any way to capture the open event since this event is fired on the window before the edit event on the grid.
The "events" of the kendo grid popup are not honoured/serialized (at least not the last time I tested this back in 2014) and so you should use the grid's Edit event to control the "Pop Up" window events
So within your grid add this:
.Events(event => event.Edit("onEdit"))
.//other grid settings here.
Then add a javascript function like this:
function onEdit(e) {
//get window object
var kendoWindow = e.container.data("kendoWindow");
kendoWindow.setOptions({
title: "I have a custom Title"
//do stuff in here
});
}
Then you can apply what ever functions you want to the window via javascript.
I do something similar to this to resize the pop up editor so it takes up 80% of the screen size regardless of the display/device.
If you have something more specific you are after then I will update my answer accordingly.
edit: If you want you can refer to this post from Telerik's own forums which is what I used when I first encountered this issue back in mid 2014.
Kendo Pop Up Editor not firing off applied events

Kendo MVC - Persist and load grid buttons

I am trying to persist my grid data and following this example.
This works very well for me but the problem is I am having Excel Import button in my grid and after loading the persisted state of the grid, the Excel Export button is disappeared.
This is my code for the grid (data persisting code is not here, it is same as the above example).
#(Html.Kendo().Grid<DtoTaskExtended>()
.Name("AdraKendoGrid")
.TableHtmlAttributes(CodeTaskKendoGrid.GetTableHtmlAttributes())
.RowAction(CodeTaskKendoGrid.GridPerRowAction)
.CellAction(CodeTaskKendoGrid.GridCellsConfigurator)
.Columns(CodeTaskKendoGrid.ColumnsConfigurator)
.ToolBar(tools => tools.Excel())
.Pageable(pager => pager.PageSizes(new int[] { 15, 50, 100, 500 })
.Info(true)
.Messages(message => message.Display("{0} - {1} " + Strings.of + "{2} " + Strings.items))
.Messages(message => message.ItemsPerPage(Strings.itemsPerPage))
.Messages(message => message.Empty(Strings.noItemsToDisplay)))
.Sortable()
.Groupable(gr => gr.Messages(message => message.Empty(Strings.kendoGroupMsg)))
.Excel(excel => excel
.AllPages(true)
.FileName("Task Grid Export.xlsx")
.Filterable(true)
.ProxyURL(Url.Action("Excel_Export_Save", "Task")) //.ForceProxy(true)
)
.Filterable()
.Reorderable(reorder => reorder.Columns(true))
.Resizable(r => r.Columns(true))
.ColumnMenu()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(10)
.Read(read => read.Action("GetTaskResult", "Task")))
.ClientDetailTemplateId("client-template")
)
Data is saved and loaded correctly, but the grid buttons (Export to Excel) button is disappeared after loading data.
How do I persist the button of the gird?
Thank you.
Hi I have a same issue like you and i solve my problem like this
function load() {
var grid = $('#gr').data("kendoGrid");
var toolBar = $("#grid .k-grid-toolbar").html();
var options = localStorage["kendo-grid-options-log"];
if (options) {
grid.setOptions(JSON.parse(options));
$("#grid .k-grid-toolbar").html(toolBar);
$("#grid .k-grid-toolbar").addClass("k-grid-top");
}
}
There is a limitation for making the toolbar persistent. A note about it from the kendo docs:
An important limitation when using the setOptions method in combination with the MVC wrappers is that any toolbar or header server templates (razor syntax #) will be lost and the layout will become incorrect once the method is invoked. Those options cannot be persisted because there is no JavaScript equivalent option for them since they contain server side logic. Consider using JavaScript initialization (instead of the MVC wrapper). An alternative is to specify the same option with the JavaScript equivalent.
Here's a possible solution:
Persist state issues
I'm not a developer, but ran across the same problem with using javascript. I had to put the entire template code in the grid options, instead of pointing to an HTML template.
I hope that points you in the right direction.
After a long research I was able to find a real and workable solution:
https://github.com/telerik/ui-for-aspnet-mvc-examples/blob/master/grid/grid-preserve-server-toolbar-template-after-set-options/GridPerserveToolbarServerTemplate/Views/Home/Index.cshtml
You need to add the following code to your View:
Razor:
#helper ToolbarTemplate() {
<a class="k-button k-button-icontext k-grid-save-changes" href="javascript:void(0)"><span class="k-icon k-update"></span>Save changes</a>
<a class="k-button k-button-icontext k-grid-cancel-changes" href="javascript:void(0)"><span class="k-icon k-cancel"></span>Cancel changes</a>
}
<script type="text/x-kendo-template" id="toolbarTemplate">
#Html.Raw(#ToolbarTemplate().ToHtmlString().Replace("#", "\\#").Replace("</scr", "<\\/scr"))
</script>
JavaScript:
<script>
//Here you define the ID of your grid
var grid = $("#grid").data("kendoGrid");
//Here you get the local settings for your case
var options = localStorage["settings"];
//To verify if there is anything stored
if (options) {
//To parse the result
var parsedOptions = JSON.parse(options);
//To display the toolbar
parsedOptions.toolbar = [
{ template: $("#toolbarTemplate").html() }
];
//To set the stored changes
grid.setOptions(parsedOptions);
}
</script>
What is the trick?
You need to get code generated the first time before saving the state (you can get it with inspect element).
And add it to the ToolbarTemplate(), after that the toolbar is going to be stored too.
Also, in the above link you can read more about headers if you want to stored them too, it will be a similar code.
The code that I showed it's fully tested and it's working 100% of cases.
If you a doubt of why this is happening, as far as I know it's related to the fact that the Toolbar is created on the server side while the states are done on the client side.

Razor - editable grid - what approach?

I am very much wondering and don't know yet what to decide to use.
So I want to have grid and to edit its row's data.
I am working with MVC3 Razor, i know for these posibilities:
- jqGrid Razor
- knockout grid Razor - i don't know how to make it editable grid? Is it possible at all, or is this just for displaying data.
- web grid , Razor
I don't like to use Telerik controls!
Would you pls advice me what is the best approach to make editable grid and what if i don't need editable grid? Why to use one or other of the ways?
And all that in Razor page.
ShieldUI's grid also supports a lot of editing modes, scenarios and sending the updates locally or to any remote endpoint.
A good example to start can be found here:
http://demos.shieldui.com/mvc/grid-editing/editing-restful-web-service
jqGrid definitely does support editing, you can see some examples at http://www.trirand.com/blog/jqgrid/jqgrid.html or read the documentation at http://www.trirand.com/jqgridwiki/doku.php?id=start
Use The Following Code For Editing and Same as Add, Delete.
For View,
#(Html.Telerik().Grid<CustomerOrderDetails>()
.Name("gvCustomerOrderDetails")
.DataKeys(keys => keys.Add(k => k.ItemID))
.Columns(column =>
{
column.Bound(i => i.ItemID).Hidden(true);
column.Bound(i => i.SalesSequenceNumber).Hidden(true);
column.Bound(i => i.ItemSequence).Hidden(true);
column.Bound(i => i.ItemName).Title("Item Name").ReadOnly();
column.Bound(i => i.Quantity).Title("Order Quantity").HtmlAttributes(new { #class = "gridTextAlignRight" });
column.Bound(i => i.ItemUnitPrice).HtmlAttributes(new { #class = "gridTextAlignRight" }).ReadOnly();
column.Bound(i => i.ItemUnitPrice).Hidden(true);
column.Bound(i => i.TotalPrice).HtmlAttributes(new { #class = "gridTextAlignRight" }).ReadOnly();
column.Command(command =>
{
command.Edit().ButtonType(GridButtonType.Image);
}).Width(80).Title("Commands");
})
.Selectable()
.DataBinding(dbBindings =>
{
dbBindings.Ajax().Select("__CustomerOrderDetailsGridBind", "CustomerInfo")
.Update("__CustomerOrderDetailsUpdate", "CustomerInfo");
})
.ClientEvents(events =>
events.OnDataBinding("onDataBinding")
.OnError("onError")
)
.Scrollable(scroll => scroll.Height(300))
)
For Control Use the following Code,
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult __CustomerOrderDetailsUpdate(CustomerOrderDetails objCustomerOrderDetails)
{
SalesItem objSalesItem = new SalesItem();
objSalesItem.SalesSequenceNumber = objCustomerOrderDetails.SalesSequenceNumber;
objSalesItem.ItemSequence = Convert.ToByte(objCustomerOrderDetails.ItemSequence);
objSalesItem.ItemID = objCustomerOrderDetails.ItemID;
objSalesItem.Quantity = objCustomerOrderDetails.Quantity;
objSalesItem.ItemUnitPrice = objCustomerOrderDetails.ItemUnitPrice;
objSalesItem.TotalPrice = objCustomerOrderDetails.ItemUnitPrice * objCustomerOrderDetails.Quantity;
objSalesItem.SalesDate = DateTime.Now;
objSalesItem.EntryBy = objLoginHelper.LogInID;
objSalesItem.EntryDate = DateTime.Now;
customerDal.UpdateSalesItem(objSalesItem);
return View(new GridModel<CustomerOrderDetails>
{
Data = customerDal.CustomerOrderDetailsInfo(objCustomerOrderDetails.SalesSequenceNumber, Helper.Active)
});
}
you can also use your Model specific Class for Add, Edit & Delete. Here ReadOnly() is used for unedited column. If you need the column value in control then use same column without ReadOnly() and just Hidden.
I think all are enjoy using this code.