I work with Telerik-MVC and I'm trying to rebind a grid when I change the value of a combobox. Everything seems working except I can't get selected value of my combobox.
Here is my code :
Grid :
#{Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Bound(o => o.Col1);
columns.Bound(o => o.Col2);
columns.Bound(o => o.Col3);
})
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_MyAction", "MyController")
)
.ClientEvents(events => events.OnDataBinding("Grid_onDataBinding"))
}
Combobox :
#(Html.Telerik().ComboBox()
.Name("ComboBox")
.HtmlAttributes(new { id = "ComboBoxCode" })
.BindTo(new SelectList(Model.GroupBy(x => x.Code)
.Select(o => o.First()).ToList(),
"Code", "Code"))
.ClientEvents(events => events
.OnChange("onComboBoxChange")
)
)
Script :
function onComboBoxChange(e) {
$("#Grid").data("tGrid").rebind();
}
function Grid_onDataBinding(e) {
var code = /* I need to get the combobox value here */;
e.data = { myCode: code };
}
Controller :
[GridAction]
public ActionResult _MyAction(string myCode)
{
Console.WriteLine("Code : " + code);
/*
Set new model here
*/
return View(new GridModel(newModel));
}
I tried things like :
var e = document.getElementById("ComboBoxCode");
var code = e.options[e.selectedIndex].text;
But it doesn't work. Can you tell me how to fix this problem and get the right value ?
The problem is that when you rebind the grid, you didn't really use the ComboBox selected value. Your onComboBoxChange function tells the grid to rebind, which it does by going to the _MyAction method. At no point did you pass in the ComboBox value. What you should do is before databinding, grab the selected value and pass it to your controller action. See http://www.telerik.com/help/aspnet-mvc/telerik-ui-components-grid-client-api-and-events.html#OnDataBinding for details. Something like this:
function Grid_onDataBinding(e) {
var combobox = $('#ComboBox').data('tComboBox');
e.data = { code: combobox.value };
}
(Note that I actually haven't done any work with the ComboBox, so this isn't tested, but this should at least get you on the right path. See ComboBox documentation.)
Related
Below is my kendo grid code
#(Html.Kendo().Grid<DataSource>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(p => p.Quote_ID).Filterable(false);
columns.Bound(p => p.Ticket_ID).Groupable(true);
columns.Bound(p => p.Channel).Groupable(true);
columns.Bound(p => p.Agent_Alias).Groupable(true).Hidden(true);
columns.Bound(p => p.Shipping_Carrier).Groupable(true).Hidden(true);
columns.Bound(p => p.Quote_ID).Title("View
Details").Groupable(false)
.Template(#<text>
#Html.ActionLink("Show Product Details", "GridRowSummary",
"GridOrderSummary")</text>);
})
From ActionLink I am trying to call Action Method of my controller.
Below My controller code
public ActionResult GridRowSummary()
{
return View();
}
Using Template will work when using Ajax bound grids e.g:
columns.Template(c => #Html.ActionLink("GridRowSummary", "GridOrderSummary", new { id = c.Id, }));
If not using Ajax bound grids use ClientTemplate attribute on the column, along with a method to display the associated data, if required e.g.:
columns.Bound(p => p.Quote_ID).Title("View Details").Groupable(false)
.ClientTemplate(#Html.ActionLink("#=Quote_ID#", "GridRowSummary", new { ID = "#=ID#" }).ToHtmlString());
There is a third method (a little messy) which allows you to add custom buttons/icons etc e.g:
columns.Bound(p => p.Quote_ID).ClientTemplate("<a href='" + #Url.Action("GridRowSummary", "GridOrderSummary", new { id = "#=Id#" }) + "' class='btn btn-primary'><i class='fa fa-eye'></i> Link</a>" );
EDIT
From looking through the FAQ section, found an even neater solution where you can pass controller name and your Quote_ID parameter (although this way does involve setting up a Javascript function):
columns.Bound(p => p.Quote_ID).ClientTemplate("#= getDetails(data) #");
<script>
function getDetails(data) {
var action = '#Url.Action("NameOfMethod", "NameOfController")';
var html = kendo.format("<a href='{0}/{1}'>Link</a>",
action,
data.Quote_ID
);
return html;
}
</script>
For client template below code works. Replace is necessary.
columns.Bound(p => p.Quote_ID).Title("View Details").Groupable(false)
.ClientTemplate(
#Html.ActionLink("#=Quote_ID#", "Summary", new { Quote_ID = "Id"
}).ToHtmlString().Replace("Id", "#=Quote_ID#"));
This solution works for me.
.Events(events =>
{
events.Change("onRowSelected");
})
function onRowSelected(e) {
debugger;
var gview = $("#grid").data("kendoGrid");
//Getting selected item
var selectedItem = gview.dataItem(gview.select());
var ticketId = selectedItem["Ticket_ID"];
window.location.href = "/GridOrderSummary/GridRowSummary?
ticketId=" + ticketId;
}
//Controller code
public class GridOrderSummaryController : Controller
{
// GET: GridOrderSummary
public ActionResult GridRowSummary(string ticketId)
{
// your code
return View();
}
}
I'm trying to set width for one of the columns kendo grid.
Here the simple code:
#(Html.Kendo()
.Grid<MyObject>()
.Name("Name")
.TableHtmlAttributes(new { style = "height: auto;" })
.Columns(c => {
c.Bound(m => m.ObjectId).Hidden();
c.Bound(m => m.Type).Title()
//Bound other fields
if (Model.Property)
{
c.Bound(m => m.Price).Title()
.HeaderHtmlAttributes(new {title = "Price"});
//Here I want to change the width of my first column
c.Container.Columns.Where(x => x.Title == "Type").FirstOrDefault.Width(200);
}
})
.Scrollable(src => src.Height(261))
.DataSource(ds => ds
.Ajax()
.Events(e => e.Error("app.ui.kendo.onGridError").RequestEnd("app.ui.project.onRequestEnd"))
.Read(r => r
.Action("Action", "Controller", new { Id = #Model.Id })
.Type(HttpVerbs.Get))))
The problem is that compiler said "Method, delegate or event expected". Are there another ways to change width after bounding the column?
To Change it after bound you have to
1) Attach an event in kendo grid like
#(Html.Kendo().Grid<SomeModel>()
.Name(Model)
.Events(e => e.DataBound("some_name"))
2) In Jquery
function some_name(e) {
// Access the column here to set the width
$('#SomeField').width(percentage)
}
This question has been answered before by Telerik themselves. Here ya go!
http://www.telerik.com/forums/change-column-widths-after-grid-created
It looks like it's unsupported out of the box, or was so in 2013 at least. Personally I would just grab the options from the grid, change the width of the column you need, and then re-apply these options to the grid.
My workplace uses Kendo for all our UI's and this is probably the easiest way I've found to edit multiple options after databound, even if they're not exposed through Kendo methods.
Code below is just an example, probably missed something but should get you started.
var grid = $("#Name").data("kendoGrid");
var opts = grid.options;
opts.columns[0].width = "1000px";
grid.setOptions(y);
I don't believe this requires a grid refresh, but it might. Also I believe you can use the string name of the field tied to the column instead of the index.
I am migrating my application form MVC Extension to the Telerik UI for ASP.NET MVC. Changed the grid to:
#(Html.Kendo().Grid((IEnumerable<vw_Client_info>)ViewBag.Clients)
.Name("Clients")
.Columns(columns =>
{
columns.Bound(c => c.ClientID).Width(30).Title("Client ID").Hidden();
columns.Bound(c => c.FullName).Width(130);
})
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(p => p.ClientID))
.Read(read => read.Action("AllClientsSelection", "Visit")))
.Selectable()
.Events(events =>
{ events.Change("onRowSelected"); })
.RowAction(row => row.Selected = row.DataItem.ClientID.Equals(ViewData["id"]))
)
To get the cell value, I used before :
function onRowSelected(e) {
var detailGrid = $('#Visit').data('tGrid');
id = e.row.cells[0].innerHTML;
fullname = e.row.cells[1].innerHTML;
$('#ClientFullName').text(fullname);
detailGrid.rebind();
}
This works with Telerik MVC Extensions , but not with the new version, I get this error:
Unable to get property 'cells' of undefined or null reference.
I tried to use:
function onRowSelected(e) {
var detailGrid = $('#Visit').data('kendoGrid');
var id = e.row.ClientID;
var fullname = e.row.FullName;
$('#ClientFullName').text(fullname);
detailGrid.rebind();
}
I get this error:
Unable to get property 'ClientID' of undefined or null reference
I found the answer:
var grid = $("#Clients").data("kendoGrid");
var selectedItem = grid.dataItem(grid.select());
id = selectedItem.ClientID;
fullname = selectedItem.FullName;
This is due to the fact that the value of 'row' in:
var id = e.row.ClientID;
is null. So you might want to check if row is null before setting ClientID or cells[0] or cells[1] to the variables. So perhaps:
var id = null;
if(e.row != null)
id = e.row.ClientId
However, I think you might need to debug and find out if you're getting any 'row' count at all, though. Because if there is no 'row' then there is no 'row.ClientId' .
Our Kendo Grid DataSource looks like this:
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(10)
.Events(events => events.Error("error_handler"))
.Model(model =>
{
model.Id(m => m.MilestoneId);
model.Field(m => m.ProjectName).Editable(false);
model.Field(m => m.Name).Editable(false);
model.Field(m => m.Status).Editable(??????);
})
For the last field (Status) we need to provide a bool value for Editable. However, I would like this value to come from the value of a property on our model. The model has a property called IsAvailable and I would like the bool to be that value.
Basically we only want the Status column to be editable if IsAvailable is true in the model.
The C# code on the model for that property is:
public bool IsAvailable{ get; set; }
Does anyone know the correct syntax to access this value?
I have tried:
model.Field(m => m.Status).Editable((model.Field(m => m.IsAvailable).ToString()).AsBool());
which compiles but does not work; it is always returning false for all cases.
Using Timothy Walter's link, I found a solution to the issue, by using the edit event as he suggested.
Here is what my code ended up looking like:
.Events(events =>
{
events.Edit("edit_handler");
})
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(10)
.Events(events => events.Error("error_handler"))
.Model(model =>
{
model.Id(m => m.MilestoneId);
model.Field(m => m.ProjectName).Editable(false);
model.Field(m => m.Name).Editable(false);
model.Field(m => m.Status).Editable(true);
})
and the Javascript is:
<script type="text/javascript" language="javascript">
function edit_handler(e) {
if (!e.model.isNew()) {
var statusDropDown = e.container.find("input[name=Status]").data("kendoDropDownList");
if (!e.model.IsAvailable) {
statusDropDown.enable(false);
}
}
</script>
It is probably easier to enable editing of this field by default, and attach to the "edit" event and disable the edit field based on the data for the current row as needed.
For an example look at the docs for the edit event on the grid.
I have a telerik grid with a dynamic data source (the grid may use up to roughly 10 totally different models for its data), so I have to build the columns dynamically as well (obviously). One of the columns in the grid (with certain models) is a double representing a time span in milliseconds. What I want to do is format this double to look like a timespan.The telerik code looks like this:
<% Html.Telerik()
.Grid(Model.DynamicGridDataSource)
.Name("statisticalGrid")
.Columns(a => GridHelper.GenerateColumns(a, Model.SelectedReport))
.DataBinding(dataBinding => dataBinding.Ajax().Select("_SelectGrid", "Reports", new { reportId = Model.ReportId, dateFrom = Model.DateFrom, dateTo = Model.DateTo, date = Model.Date, AvailablePlans = Model.AvailablePlans }))
.Sortable(GridSortSettingsBuilder => GridHelper.SortColumns(GridSortSettingsBuilder,
Model.DynamicGridDataSource.GetType(),
Model.SelectedReport))
.Filterable()
.Pageable(page => page.PageSize(25))
.Reorderable(reorder => reorder.Columns(true))
.Groupable
(
groupingSettingsBuilder => GridHelper.GroupColumns(groupingSettingsBuilder,
Model.DynamicGridDataSource.GetType(),
Model.SelectedReport)
)
.ClientEvents(events => events
.OnColumnReorder("onReorder"))
.Render();
and GridHelper.GenerateColumns looks something like this:
public static void GenerateColumns(GridColumnFactory<dynamic> columnFactory, Company.Product.Data.Entity.Report reportStructure)
{
foreach (var columnLayout in reportStructure.ReportCols.OrderBy(o => o.ColumnSequence))
{
var columnBuilder = columnFactory.Bound(columnLayout.ColumnType);
if (columnLayout.ColumnType.Equals("SessionLength") ||
columnLayout.ColumnType.Equals("AverageTime") ||
columnLayout.ColumnType.Equals("TotalTime") ||
columnLayout.ColumnType.Equals("CallTime"))
{
// disable grouping
columnBuilder.Groupable(false);
string dataBindProperty = columnLayout.ColumnType;
if (columnLayout.DataFormat == "{0:T}")
{
//Even though the format looks like time ({0:T}), its actually a double which needs to be formatted here to look like a TimeSpan
}
}
if (!string.IsNullOrEmpty(columnLayout.Label))
{
columnBuilder.Title(columnLayout.Label);
}
if (columnLayout.DataFormat != null && columnLayout.DataFormat == "{0:P}")
{
columnBuilder.Format("{0:P}");
}
if (columnLayout.SumIndicator)
{
if (columnLayout.DataFormat == "{0:T}")
{
AddAggregateToColumnTimeSpan(columnBuilder, Aggregate.Sum);
}
else
{
AddAggregateToColumn(columnBuilder, Aggregate.Sum);
}
}
if (columnLayout.HideIndicator)
{
columnBuilder.Column.Hidden = true;
}
}
}
I was able to format the footer correctly, but I didn't know how to format the rest of the column, since out of the context of the telerik code I don't have access to the item iterator or anything. Any suggestions/ideas? Maybe columnFactory.Bound(columnType).Format(/*something*/)?
You said, "the grid may use up to roughly 10 totally different models for its data", so perhaps instead of trying to represent all those models in one grid, you have one grid for each model. You could put each grid in it's own partial view with the main view using some mechanism for deciding which partial view to load. Here is a simple example.
Controller
public ActionResult DynamicReport
{
//Get your Model
Model.model1 = model_01 = Model.DynamicGridDataSource.GetDynamicModel()
//Get the name of what model is being returned so view knows which
//partial view to load
ViewBag.Message = model_01.Name
...
return View(model_01)
}
In the view have some conditional logic to chose which partial view to load.
View
<h2>View</h2>
#{
string pView = "~/Views/Grid/Partial_01.cshtml";
switch(ViewBag.Message)
{
case "p02":
pView = "~/Views/Grid/Parital_02.cshtml"
break;
.....
}
}
#Html.Partial(pView)
PartialView_01
#model List<Models.Misc>
#(Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Bound(a => a.Id).Width(120);
columns.Bound(a => a.Name).Width(100);
columns.Bound(a => a.Value).Format("{0:#,##0.00}").Width(100).Title("Price");
})
)
PartialView_02
#model List<Models.Temp>
#(Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Bound(o => o.Name)
.Aggregate(aggregates => aggregates.Count())
.FooterTemplate(#<text>Total Count: #item.Count</text>)
.GroupFooterTemplate(#<text>Count: #item.Count</text>);
columns.Bound(o => o.Start)
.Template(#<text>#item.Start.ToShortDateString()</text>)
.Aggregate(aggreages => aggreages.Max())
.FooterTemplate(#<text>Max: #item.Max.Format("{0:d}")</text>)
.GroupHeaderTemplate(#<text>Max: #item.Max.Format("{0:d}")</text>)
.GroupFooterTemplate(#<text>Max: #item.Max.Format("{0:d}")</text>);
columns.Bound(o => o.Value)
.Width(200)
.Aggregate(aggregates => aggregates.Average())
.FooterTemplate(#<text>Average: #item.Average</text>)
.GroupFooterTemplate(#<text>Average: #item.Average</text>);
columns.Bound(o => o.tsMilliseconds)
.Width(100)
.Aggregate(aggregates => aggregates.Sum())
.Template(#<text>#TimeSpan.FromMilliseconds(#item.tsMilliseconds)</text>)
.Title("TimeSpan")
.FooterTemplate(
#<text>
<div>Sum: #TimeSpan.FromMilliseconds(#Convert.ToDouble(#item.Sum.Value.ToString())) </div>
</text>)
//header if you group by TimeSpan
.GroupHeaderTemplate(#<text>#item.Title: #item.Key (Sum: #TimeSpan.FromMilliseconds(#Convert.ToDouble(#item.Sum.Value.ToString())))</text>)
//footer for grouping
.GroupFooterTemplate(#<text>Sum: #TimeSpan.FromMilliseconds(#Convert.ToDouble(#item.Sum.Value.ToString()))</text>);
})
.Sortable()
.Groupable(settings => settings.Groups(groups => groups.Add(o => o.Start)))
)
And so on, for each different model. With each model having its own partial view you can easily format each grid to fit its model while still having only one main view.