Telerik MVC Grid. How to bind to a nullable column - asp.net-mvc-3

I have a grid with a column which contains a nullable data
Html.Telerik().Grid(Model)
.Columns(columns =>
{
columns.Add(o => o.Foo);
}
That works, but shows something like [object Object] on the client. Supposedly it should show Foo.Name, I tried to override Foo's ToString() - didn't work, I tried to change that into columns.Add(o => o.Foo.Name);, but then it doesn't want to render the entire grid.
I tried to use columns.Bound(o => o.Foo.Name); - no results. BTW, what's the difference between Add() and Bound() ?

I would use the Template of the column to do this. Here is some code
Html.Telerik().Grid(Model)
.Columns(columns =>
{
columns.Add(o => o.Foo).Template(o =>
{
%>
<%= o.Foo != null ? o.Foo.Name : "" %>
<%
})
.ClientTemplate("<#= Foo != null ? Foo.Name : '' #>");
}

Assuming that Model is a collection of RowData instances, you could add a getter FooName to the RowData class:
public class RowData
{
public string FooName
{
get { return Foo == null ? null :: Foo.Name; }
}
}
Then you can bind to FooName:
Html.Telerik().Grid(Model)
.Columns(columns =>
{
columns.Bound(o => o.FooName);
}
I can't test it at the moment. Possibly you need a setter as well. It doesn't need to do anything.
And I have no idea what Add() does. I've never used it.

Related

Kendo Grid: Toolbar template issue

I have a grid that lists Road information and want a Toolbar Template that will allow me to filter the roads by choosing a Concession from a DropDownList. Something like this
My code:
CSHTML
<div id="datagrid">
#(Html.Kendo().Grid<SustIMS.Models.RoadModel>()
.Name("datagrid_Roads")
.Columns(columns =>
{
columns.Bound(r => r.RoadCode).Title(ViewBag.lblCode).Width(140);
columns.Bound(r => r.RoadType).Title(ViewBag.RoadType).Width(140);
columns.Bound(r => r.RoadMediumDescription).Title(ViewBag.lblDescription);
columns.Bound(r => r.ConcessionCode).Title("CCode").Hidden();
columns.Bound(r => r.ConcessionMediumDescription).Hidden().Title(ViewBag.Concession);
})
.ToolBar(toolbar =>
{
toolbar.Template(#<text>
<div class="toolbar">
<label class="category-label" for="category">Concessão:</label>
#(Html.Kendo().DropDownList()
.Name("concessions")
.OptionLabel("All")
.DataTextField("ConcessionMediumDescription")
.DataValueField("CCode")
.AutoBind(false)
.Events(e => e.Change("concessionChange"))
.DataSource(ds =>
{
ds.Read("ConcessionFiltering", "MasterData");
})
)
</div>
</text>);
})
.HtmlAttributes(new { style = "height: 534px;" })
...
)
)
</div>
<script type="text/javascript">
function concessionChange() {
var value = this.value(),
grid = $("#datagrid_Roads").data("kendoGrid");
if (value) {
grid.dataSource.filter({ field: "ConcessionMediumDescription", operator: "eq", value: value });
} else {
grid.dataSource.filter({});
}
}
Controller
public ActionResult ConcessionFiltering()
{
ConcessionModel cm = new ConcessionModel();
var aux = cm.getConcessions();
return Json(aux.concessions.Select(c => c.concession.mediumDescription).Distinct(), JsonRequestBehavior.AllowGet);
}
This is the current result:
The list is filled with the word "undefined" 16 times, which is the number of concessions I currently have. When I select one of the undefined options, it shows the actual name of the concession, refreshes the grid but doesn't filter it.
I want the list to show the concession names and to filter the grid by concession as I select one of them. What am I missing?
change this
return Json(aux.concessions.Select(c => c.concession.mediumDescription).Distinct(), hJsonRequestBehavior.AllowGet);
to
return Json(aux.concessions.Select(c => new ConcessionModel { Description = c.concession.mediumDescription }).Distinct(), JsonRequestBehavior.AllowGet);
First, double check what Json you are returning from the controller method. It looks like your ConcessionMediumDescriptions may have no data in them.
Second, it looks like, in your controller, that you are returning a list of "ConcessionMediumDescription" data objects.
I am guessing it looks like this...
{ConcessionMediumDescription: {
CCode: 'mycode',
...
}
}
You may consider returning a title field as a part of this Json and to use that for the text field of your dropdown. This is just me guessing from what you are returning in that controller.
Ideal Json would be somting like this...
[{
{{id: 'id1'},{text: 'text1'}},
{{id: 'id2'},{text: 'text2'}}
}]
And you defind your dropdown as such.
.DataTextField("text")
.DataValueField("id")
You have to do json return line like this.
return Json(aux.concessions.Select(c => new { Value = c.concession.DATAVALUE, Text = c.concession.DATATEXT }), JsonRequestBehavior.AllowGet);
Just change DATAVALUE and DATATEXT

Telerik MVC Grid with multiple checkboxes in a single row, single column

I am trying to construct a Telerik MVC grid and am having great difficulty constructing a row which contains more than one checkbox in a grid cell.
So far my attempt has gotten me only half way there. It displays a single row with a whole bunch of check boxes in the first cell, but when I want to add a record via the "Insert" toolbar button, I get a blank line.
Here is my model and view. Any help would be appreciated.
public class MyModel {
public MappingsModel[] Mappings { get; set; }
public class MappingsModel {
public CustomerRoleModel[] CustomerRoles { get; set; }
public int[] SelectedCustomerRoleIds { get; set; }
}
public class CustomerRoleModel {
public int Id { get; set; }
public string Name { get; set; }
public bool Selected { get; set; }
}
}
#(Html.Telerik().Grid(Model.Mappings)
.Name("rules-grid")
.DataKeys(keys =>
{
keys.Add(x => x.Id);
})
.DataBinding(dataBinding =>{
dataBinding.Ajax()
.Select("RuleList", "MyController")
.Insert("RuleInsert", "MyController")
.Update("RuleUpdate", "MyController")
.Delete("RuleDelete", "MyController");
})
.Columns(columns =>
{
columns.Bound(x => x.CustomerRoles)
.Template(#<text>
#foreach (var role in #item.CustomerRoles) {
<text><input type="checkbox" value="#(role.Id)" id="role_#(role.Id)" name="SelectedCustomerRoleIds" disabled="disabled" </text>
if(role.Selected) {
<text>checked="checked"</text>
}
<text>/><label for="role_(#role.Id)">#role.Name</label><br /></text>
}
</text>
)
.Width(500);
columns.Command(commands =>
{
commands.Edit();
commands.Delete();
})
.Width(180);
})
.ToolBar(commands => commands.Insert())
.EnableCustomBinding(true));
Update
I have tried the suggestion offered by #howcheng but this renders the checkboxes like so:
[object Object],[object Object],[object Object],[object Object]
The data I'm getting back via Ajax looks valid even though it doesn't looked correct below. Any ideas?
data: [{Name:null, Age:0,…}]
0: {Name:null, Age:0,…}
Age: 0
CustomerRoles: [{Name:Administrators, Selected:false, Id:1}, {Name:Forum Moderators, Selected:false, Id:2},…]
0: {Name:Administrators, Selected:false, Id:1}
1: {Name:Forum Moderators, Selected:false, Id:2}
2: {Name:Guests, Selected:false, Id:4}
3: {Name:Registered, Selected:false, Id:3}
Id: 1
Name: null
total: 1
What you've done is define a display template. In order to edit, you need a custom editor template. Create a partial view in your /Views/Shared/EditorTemplates directory and call it something like "CustomerRoles.cshtml".
Editor template:
#model CustomerRoleModel[]
#foreach (CustomerRoleModel crm in Model)
{
// checkbox code goes here
}
Grid code:
#(Html.Telerik().Grid(Model.Mappings)
.Columns(columns =>
{
columns.Bound(x => x.CustomerRoles)
.Template(#<text>
#item.Select(r => r.Name).Aggregate((s1, s2) => string.Format("{0}, {1}", s1, s2))); // this is for display only
</text>)
.ClientTemplate("<#= CustomerRolesClientTemplate(data) #>")
.EditorTemplateName("CustomerRoles"); // this loads the editor template
// additional columns
})
// additional grid code
)
Javascript function for client template:
function CustomerRolesClientTemplate(data) {
var value = '';
var first = true;
for (var i = 0; i < data.length; i++) {
if (!first) value += ', ';
value += data.Name;
first = false;
}
return value;
}
In your Google searching, you might come across the Telerik documentation -- DON'T RELY ON THIS because it's a little out of date (it's not written for Razor and you don't need to use the [UIHint] attribute, for example).

Telerik MVC Grid in Razor view, GridCommand not populating

I have a telerik mvc grid in a Razor view. I am using custom server binding. My issue is that on paging and sorting the GridCommand object properties "Page", "PageSize" & "SortDescriptors" are not getting the correct value. The funny thing is that the exact same code works for an aspx view. Since this is a new view, I have started using "Razor".
My view is -
#(Html.Telerik().Grid((IEnumerable<Mitek.MobileImaging.AdminSite.Models.ImagingTransactionModel>)ViewData["DeficientGridView"])
.Name("DeficientImagesGrid")
.DataBinding(databinding => databinding.Server()
.Select("ViewDeficientImages", "SuperAdmin", new { orgId = ViewData["OrgId"], beginDate = ViewData["BeginDate"], endDate = ViewData["EndDate"], searchString = ViewData["SearchString"] }))
.DataKeys(keys => keys.Add(o => o.TranId))
.EnableCustomBinding(true)
.BindTo((IEnumerable<Mitek.MobileImaging.AdminSite.Models.ImagingTransactionModel>)ViewData["DeficientGridView"])
.Columns(
columns =>
{
columns.Template(
#<text>
<a href="#Url.Action("DeficientImageDetails", "SuperAdmin", new { id = item.TranId }) ">
<img alt="Deficient Image Details" src= "#Url.Content("~/Content/ImagesUI/detail_icon.gif")" style="border:0px" /></a>
</text>
).Title("Actions").Width(75);
columns.Bound(o => o.TranId).Hidden(true);
columns.Bound(o => o.user_email).Title("User Email").Width(250);
columns.Bound(o => o.xml_config_name).Title("Job File").Width(200);
columns.Bound(o => o.datetime_created).Title("Date Created").Format("{0:MM/dd/yyyy}").Width(200);
columns.Bound(o => o.short_note).Title("Note").Width(200);
columns.Bound(o => o.iqa_code).Title("IQA Code").Width(200);
}).HtmlAttributes(new { style = " font-family:arial; font-size: .9em; " })
.Sortable()
.Pageable(paging => paging.Position(GridPagerPosition.Bottom)
.Style(GridPagerStyles.NextPreviousAndNumeric)
.Total((int)ViewData["DeficientImagesCount"])
.PageSize(25))
)
The controller looks like
[GridAction(GridName = "DeficientGridView")]
public ActionResult ViewDeficientImages(DeficientImagesViewModel model, GridCommand command, string button)
{
//Some Code......;
GridCommand myCommand = new GridCommand() { PageSize = 25 };
}
The command object never has any values for command.Page, command.SortDescriptors at the time of paging or sorting. Please note that the exact same code works in a asps page.
Please help.
Thanks,
SDD
Can you check if it has to do with [GridAction(GridName = "DeficientGridView"] attribute and your grid name being different.Name("DeficientImagesGrid") ?
You have to change [GridAction(EnableCustomBinding = true, GridName = "DeficientImagesGrid")] to this. I have the same problem today and i found this is working. If you don't specify the GridName then you will not get GridCommand.
gridCommand is NOT populated when the query string parameters are QueueGrid-size=2&QueueGrid-page=3
gridCommand is populated when the query string parameters are size=2&page=3

Bound column to a list and loop...Is it possible at all?

I'm a novice Telerik Grid user and was wondering if it is a possibility to use Telerik Grid such that I bound one column to a string array and then somehow loop through it to show the array items as list.
StoreViewModel:(One store can have multiple displays)
public string Name { get; set; }
public string[] StoreDisplays { get; set; }
Controller:
private IQueryable<StoreViewModel> GetRetailers()
{
var stores = from sg in db.Store
select new RetailerViewModel
{
Name = sg.sg_Name,
Name = sg.sg_Name,
StoreDisplays = ( from ca in db.Categories.Where(item => item.c_ParentId == null)
join sd in db.StoreDisplays.Where(item => item.sd_StoreGroupId == sg.sg_Id)
on ca.c_Id equals sd.sd_CategoryId into gj
from subpet in gj.DefaultIfEmpty()
select (ca.c_Name)).ToArray<string>()
};
return stores;
}
Index.aspx
<%= Html.Telerik().Grid<StoreLocatorBackOffice.Models.RetailerViewModel>(Model) <br/>
.Name("Grid")
.Columns(columns =>
{
columns.Bound(sg => sg.Name)
columns.Bound(sg => sg.StoreDisplays);
})
.DataBinding(dataBinding => dataBinding
.Ajax()
.Select("_Index", "Retailer", true)
)
.Scrollable(scrolling => scrolling.Enabled(false))
.Sortable(sorting => sorting
.OrderBy(sortOrder => sortOrder.Add(p => p.Name).Ascending()))
.Pageable(settings => settings.PageSize((int)ViewData["pageSize"]))
.Filterable(filtering => filtering.Enabled(true))
.Footer(true)
%>
Currently it Displays like this.
Name Store Display
Store#1 Display#1Display#2Display#6
Store#2 Display#3Display#9
And want to some how show it as follows.
Name Store Display
Store#1 Display#1
Display#2
Display#6
Store#2 Display#3
Display#9
Instead of:
columns.Bound(sg => sg.StoreDisplays);
You should bind using a template. That template can be whatever you want, (even another grid).
Here's a template that will put an image in:
columns.Template(c => {
%>
<img
alt="<%= c.CustomerID %>"
src="<%= Url.Content("~/Content/Grid/Customers/" + c.CustomerID + ".jpg") %>"
/>
<%
});
Changing that template to display a list should be simple.
Look here for more info.
I would use HTML tags in to make data to spread vertically
For example
StoreDisplays = "
<ul>
<li>Display#1</li>
<li>Display#2</li>
<li>Display#3</li>
</ul>";
.Columns(columns =>
{
columns.Bound(o => o.StoreDisplays).ClientTemplate("<#= StoreDisplays #>");
}

View Database Columns via Checkbox WebFrom and Cookies

I am using the telerik MVC template and have a database that has a huge number of columns and the telerik grid does not have a horizontal scroll bar, so I created checkboxes for the user to select exactly what columns they want to view. It works well enough in that when I first go to the page it shows the checkboxes at top with the Apply button and the grid view underneath. Because nothing has been submitted from the WebForm, the grid view shows all the columns. Before adding the cookies, the user only had to press apply once for only those columns to appear. However, if the user then tried to sort or filter one of these columns, it would revert back to showing all of the columns. So, I created a cookie to store the selected information. Unfortunately, this only helped with the selection of the first filter. If a second filter is used, it would, again, show all of the columns instead of just the ones selected. Furthermore, the user now has to press apply twice for their selections to show properly on the grid view. Here is a brief explanation of how I have everything coded:
Index View
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm("Index", "Order"))
{ %>
<p>
<%= Html.CheckBox("osurr", true, "Ad Number")%>Ad Number //I set this and a few other columns to default to true
<%= Html.CheckBox("csurr", false, "Customer Number")%>Customer Number
<%= Html.CheckBox("rhosurr", false, "RHO Number")%>RHO Number
<%= Html.CheckBox("lockid", false, "Lock ID")%>Lock ID
//And several more
</p>
<input type="submit" value="Apply" />
<% } %>
<%
Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Template(o =>
{
%>
<%=Html.ActionLink("Detail", "Edit", new { id = o.osurr })%>
<%
}).Width(25);
if (Request.Cookies["DBCols"]["csurr"] != null)
{
if (Request.Cookies["DBCols"].Values["csurr"].Equals("True")) { columns.Bound(o => o.csurr).Title("Cust. No."); }
}
if (Request.Cookies["DBCols"]["rhosurr"] != null)
{
if (Request.Cookies["DBCols"].Values["rhosurr"].Equals("True")) { columns.Bound(o => o.rhosurr).Title("RHO No."); }
}
if (Request.Cookies["DBCols"]["lockid"] != null)
{
if (Request.Cookies["DBCols"].Values["lockid"].Equals("True")) { columns.Bound(o => o.lockid).Title("Lock ID"); }
}
//And again, several more.
})
.Groupable(grouping => grouping.Enabled(true))
.Resizable(resizing => resizing.Columns(true))
.Filterable(filter => filter.Enabled(true))
.Sortable(sorting => sorting.Enabled(true))
.Pageable(paging => paging.Enabled(true).PageSize(25))
.Render();
%>
</asp:Content>
Controller
public ActionResult Index(bool? csurr, bool? rhosurr, bool? lockid /* And Several More */)
{
ViewData["csurr"] = csurr ?? true;
ViewData["rhosurr"] = rhosurr ?? true;
ViewData["lockid"] = lockid ?? true;
if ((bool)ViewData["csurr"]) { DBCols.Values["csurr"] = (ViewData["csurr"].ToString());
}
else { DBCols.Values["csurr"] = "False"; }
if ((bool)ViewData["rhosurr"]) { DBCols.Values["rhosurr"] = (ViewData["rhosurr"].ToString()); }
else { DBCols.Values["rhosurr"] = "False"; }
if ((bool)ViewData["lockid"]) { DBCols.Values["lockid"] = (ViewData["lockid"].ToString()); }
else { DBCols.Values["lockid"] = "False"; }
//And Several more
var db = new MillieOrderDB();
var listView = from m in db.vw_cadords
orderby m.createstamp descending
select m;
return View(listView);
}
I am working just in the Index ActionResult for now to keep things in one place while I figure out how to get this all to work. Anyone have any ideas why I am having to press apply twice, why I can not use more than one filter, and how to avoid this?
It turns out that the reason I had to hit apply twice and why when applying more than one filter caused issues was because I had everything in the Index ActionResult. Once I moved all the form data to its own ActionResult and then did RedirecttoAction("Index"), everything worked fine!

Resources