In part 2 of this series on the Telerik MVC Grid control, we discussed the back-end code for supporting the master level of our grid. Here’s a list of tasks we need to take care of for the detail grid:
- Implementing the detail view within the grid component definition.
- Implementing additional JavaScript functions to handle the detail grid events.
- Implementing a View Model to support the detail grid.
- Implementing several controller actions to support grid CRUD functionality.
- Implementing helper methods.
If I don’t list all the code below (mainly, the controller actions), you can get all of it by downloading the full example, or keep up with any changes on GitHub.
Extending the Grid Declaration in the View
Realize that the detail grid generate detail grids (plural) at runtime, for each expanded master row. The way the detail level of a grid is handled, it’s pretty much another sophisticated “client template” hanging off the master row, built from another grid. That’s why the whole definition is wrapped in a ClientTemplate option:
.DetailView(details => details .ClientTemplate(Html.Telerik() .Grid<OrderViewModel>() .Name("Orders_<#= CustomerId #>")
Note the very explicit name we’re giving to each detail grid instance (via the Name option), making use of the master row’s CustomerId value. You’ll see its importance later on.
We’ll specify the detail columns next, starting with a column that contains our edit and delete buttons. Notice that we made sure only the DatePlaced column is filterable. In order to allow filtering at all, you must first apply this option to the grid (shown later), and then explicitly turn off filtering for the columns you don’t want it for. We’re also specifying a format for the DatePlaced column, and overriding some default column titles:
.Columns(columns => { columns.Command(commands => { commands.Edit().ButtonType(GridButtonType.Image); commands.Delete().ButtonType(GridButtonType.Image); }).Width(80); columns.Bound(o => o.DatePlaced) .Format("{0:MM/dd/yyyy}"); columns.Bound(o => o.OrderSubtotal) .Title("Subtotal") .Filterable(false); columns.Bound(o => o.OrderTax) .Title("Tax") .Filterable(false); columns.Bound(o => o.OrderTotal) .Title("Total") .Filterable(false); columns.Bound(o => o.OrderChannelName) .Title("Channel") .Filterable(false); })
Similar to what we did in the master grid for customers, we’re going to want to support inserting new rows for orders at the detail level:
.ToolBar(commands => commands.Insert() .ButtonType(GridButtonType.ImageAndText) .ImageHtmlAttributes(new { style = "margin-left:0;" }))
As in the master grid, we need to specify the DataBinding options; declaring the AJAX actions that the grid will call when performing CRUD operations on the detail rows. We’re also passing in customerId, since that’s needed for each method.
- In the Select method, the customerId is used for deciding which customer to load the orders for.
- In the Insert method, the customerId is used for deciding which customer to add a new order for.
- In the Update method, the order is an Entity Framework navigation property of a customer, so customerId is used for fetching the customer.
- In the Delete method, the order is an Entity Framework navigation property of a customer, so customerId is used for fetching the customer.
.DataBinding(dataBinding => dataBinding.Ajax() .Select("AjaxOrdersForCustomerHierarchy", "Home", new { customerId = "<#= CustomerId #>" }) .Insert("AjaxAddOrder", "Home", new { customerId = "<#= CustomerId #>" }) .Update("AjaxSaveOrder", "Home", new { customerId = "<#= CustomerId #>" }) .Delete("AjaxDeleteOrder", "Home", new { customerId = "<#= CustomerId #>" }))
Now, since orderId uniquely identifies an order, we need to specify that as a DataKeys parameter used by both the Update and Delete methods:
.DataKeys(keys => keys .Add(o => o.OrderId) .RouteKey("OrderId"))
We’ll wire up our grid events next (discussed later):
.ClientEvents(events => events .OnError("onError") .OnDataBound("onDataBoundOrders") .OnEdit("onEditOrders"))
We’ll finish off our grid definition by making it pageable, with 15 rows per page, support keyboard navigation, specify that the detail grid is editable using a popup window, and making it sortable and filterable (keeping in mind that we shut off most filtering at the column level). Note that since this is actually a ClientTemplate, the whole detail grid needs to converted to an HTML string. Finally, we need to tack on a Render command, otherwise the grid won’t get displayed at all. For some reason, some examples on Telerik’s site omit this.
.Pageable(pagerAction => pagerAction.PageSize(15)) .KeyboardNavigation() .Editable(editing => editing.Mode(GridEditMode.PopUp)) .Sortable() .Filterable() .ToHtmlString() )) .Render();
Slight Detour — Fixing a Validation Bug in the Master Grid
Before we get to the supporting detail grid code, I want to revisit an issue I alluded to in part 2. Again, here is the CustomerViewModel:
public class CustomerViewModel { [ScaffoldColumn(false)] public int CustomerId { get; set; } [Required] [DisplayName("Account Number")] public string AccountNumber { get; set; } [Required] [Remote("CheckDuplicateCustomerName", "Home", AdditionalFields = "CustomerId, FirstName, MiddleName", ErrorMessage = "This name has already been used for a customer. Please choose another name.")] [DisplayName("Last Name")] public string LastName { get; set; } [Required] [Remote("CheckDuplicateCustomerName", "Home", AdditionalFields = "CustomerId, LastName, MiddleName", ErrorMessage = "This name has already been used for a customer. Please choose another name.")] [DisplayName("First Name")] public string FirstName { get; set; } [DisplayName("Middle Name")] [Remote("CheckDuplicateCustomerName", "Home", AdditionalFields = "CustomerId, LastName, FirstName", ErrorMessage = "This name has already been used for a customer. Please choose another name.")] public string MiddleName { get; set; } [DisplayName("Middle Initial")] public string MiddleInitial { get; set; } }
If you recall, I mentioned that any fields we mark with the [ScaffoldColumn(false)] attribute will not be displayed in the grid nor on the pop-up edit dialog used when we edit or add a customer. But there’s an additional side effect to us using this on the CustomerId field — our remote validation CheckDuplicateCustomerName method always returns a duplicate error, even if we’re editing an existing record. We’re passing CustomerId as an AdditionalFields field because we’re using it to allow us to ignore a duplicate error if the existing record is the current customer record. But, as it turns out, since we’re using [ScaffoldColumn(false)], it also hides CustomerId from the AdditionalFields parameter. Null is being passed into the validation method. So we have to do two things:
- Remove [ScaffoldColumn(false)] from CustomerId in the view model. Unfortunately, this causes CustomerId to be editable in the pop-up edit and add dialogs. So, we also need to…
- …add the following line to the onEditCustomers JavaScript function (the OnEdit master grid event handler):
$(e.form).find("#CustomerId").closest(".editor-field").prev().andSelf().hide();
Now we’ve forced CustomerId off of the pop-up, yet we can continue to use it in our remote validation method.
Detail Grid Events
Now that we solved that issue, here are the event handlers for the detail grid. I’m also including the replaceDeleteConfirmation helper function that’s shared with the master grid. The OnError event handler is also reproduced here, since it’s shared by both the master and detail grids as well. We’re using onError to display serious issues (normally caught in the catch blocks in controller actions), that we’re stuffing in the response header. I’d normally handle these more gracefully, but this is fine for a “quick & dirty”:
function onExpandCustomer() { $(".t-detail-cell").css({ "padding-left": "80px", "padding-bottom": "30px" }); } function onDataBoundOrders() { $(this).find(".t-grid-add").first().text("Add new Order").prepend("<span class='t-icon t-add'>"); replaceDeleteConfirmation(this, "Order"); } function onEditOrders(e) { var popup = $("#" + e.currentTarget.id + "PopUp"); var popupDataWin = popup.data("tWindow"); popup.css({ "left": "700px", "top": "400px" }); //popupDataWin.center(); // Use if you'd rather center the dialog instead of explicitly position it. if (e.mode == "insert") popupDataWin.title("Add new Order"); else popupDataWin.title("Edit Order"); var url = '@Url.Action("GetOrderChannels", "Home")'; var orderChannel = $('#OrderChannelId').data('tDropDownList'); orderChannel.loader.showBusy(); $.get(url, function (data) { orderChannel.dataBind(data); orderChannel.loader.hideBusy(); orderChannel.select(function (dataItem) { if (e.mode == 'edit') { return dataItem.Value == e.dataItem['OrderChannelId']; } else { return dataItem.Value == 1; // Default to Phone. } }); }); } function replaceDeleteConfirmation(item, itemType) { var grid = $(item).data('tGrid'); $(item).find('.t-grid-delete').click(function () { grid.localization.deleteConfirmation = "Are you sure you want to delete this " + itemType + "?"; }); }
Note that dynamically changing the “Add” button text has to be done differently for the detail grid than we did for the master. If you recall, we were able to change the button text for the master grid directly in the $(document).ready function. That’s because the button only exists once in the entire page. But since each master row requires its own “Add” button for adding orders, we have to change the button text as we databind the order rows for each customer, in the onDataBoundOrders event handler for OnDataBound. We’re also dynamically changing the “Delete” confirmation text in this function.
The other interesting function is the event handler for OnEdit, onEditOrders. We’re explicitly positioning the pop up dialog here, by first grabbing a reference to the pop up. You’l notice that we’re referencing the event parameter currentTarget.id. This is a reason why it’s important to uniquely name each detail grid, as mentioned earlier.
var popup = $("#" + e.currentTarget.id + "PopUp");
Once we have a reference to the pop up dialog, we need to grab a reference to its window (yes, although it appears redundant, the window is just a portion of the entire dialog).
var popupDataWin = popup.data("tWindow");
Now that we have a reference to each, we can dynamically position the pop up, either explicitly, or centering it by calling the undocumented center function of its window:
popup.css({ "left": "700px", "top": "400px" }); //popupDataWin.center(); // Use if you'd rather center the dialog instead of explicitly position it.
We’re also dynamically changing the pop up dialog’s title bar, depending upon the edit mode:
if (e.mode == "insert") popupDataWin.title("Add new Order"); else popupDataWin.title("Edit Order");
Using an Editor Template
Next, since we’re using a drop down list for the order channel, we’re dynamically populating the list. First, we build out the action we’re going to call via AJAX. Next, we create a reference to the drop down list. The next line of code displays an animated progress indicator for the AJAX call, which follows. Once the AJAX call completes, we bind the result to the list, get rid of the progress indicator, and initialize the currently selected order channel in the list:
var url = '@Url.Action("GetOrderChannels", "Home")'; var orderChannel = $('#OrderChannelId').data('tDropDownList'); orderChannel.loader.showBusy(); $.get(url, function (data) { orderChannel.dataBind(data); orderChannel.loader.hideBusy(); orderChannel.select(function (dataItem) { if (e.mode == 'edit') { return dataItem.Value == e.dataItem['OrderChannelId']; } else { return dataItem.Value == 1; // Default to Phone. } }); });
The above loading of the order channel drop down implies the use of an editor template. We told the view to use an editor template for the Channel property by applying the [UIHint(“OrderChannel”)] attribute to it. Here’s the template code we’re using when displaying the editor pop up (which must be named OrderChannel.cshtml in order for the view and UIHint to find it):
@(Html.Telerik().DropDownList() .Name("OrderChannelId") .HtmlAttributes(new { style = "width:400px" }) ) <p />
We happen to be making use of Telerik’s drop down list in this same MVC extension library. If you have experience with this control, you may be wondering why we didn’t make use of the DataBinding method to load the channels into the list. Unfortunately, by the time the data is loaded, it’s too late to initialize the selected item. Therefore, we’re explicitly making the AJAX call within the onEditOrders event handler.
Here’s the order view model. Also note that I’m not validating DatePlaced, aside from making it a required field. I leave that as an exercise for you:
public class OrderViewModel { [ScaffoldColumn(false)] public int OrderId { get; set; } [ScaffoldColumn(false)] public int CustomerId { get; set; } [Required] [DisplayName("Order Placed")] [DataType(DataType.Date)] public DateTime? DatePlaced { get; set; } [Required] [DisplayName("Subtotal")] public decimal? OrderSubtotal { get; set; } [Required] [DisplayName("Tax")] public decimal? OrderTax { get; set; } [Required] [DisplayName("Total")] public decimal? OrderTotal { get; set; } [ScaffoldColumn(false)] public int OrderChannelId { get; set; } [DisplayName("Channel")] [UIHint("OrderChannel")] public string OrderChannelName { get; set; } }
Some Final Cosmetic Touches
There is one more event handler I added to show how you can dynamically position the detail grid. First, I added a declaration for an additional master grid event handler, OnDetailViewExpand. You may have seen a previous article I wrote on how to take advantage of this event in other ways:
.OnDetailViewExpand("onExpandCustomer")
This event handler simply adjusts the padding of the detail cell (which is actually the detail order grid for a customer):
function onExpandCustomer() { $(".t-detail-cell").css({ "padding-left": "80px", "padding-bottom": "30px" }); }
Sample Application Download
Well, that completes my three part series. Again, you can download a full sample application, or keep up with possible changes on GitHub.
That’s the basics for creating a master / detail Telerik MVC grid, with a few extras thrown in to show you how to work around some idiosyncrasies. You can pretty much add additional detail levels in the same manner. Like I’ve mentioned, this is not the only way to go about it, but it has worked for me. If you have other ideas, please let me know.
Wow, thanks for this detailed write-up! Despite the fact that I’ve been using this control for a few months, I learned quite a few new things from reading your series.
Firstly, thanks for the excellent writeup. I think this is the only ‘real’ tutorial on the Telerik MVC grid available online.
I tried downloading and running the app. VS2010 upgraded the project to 2012.1.214.340 version of telerik controls. This is the error I get when I try to debug the app.
System.Data.EntityException was unhandled by user code
HResult=-2146233087
Message=The underlying provider failed on Open.
Source=System.Data.Entity
StackTrace:
at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
at System.Data.EntityClient.EntityConnection.Open()
at System.Data.Objects.ObjectContext.EnsureConnection()
at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Sample.WebUI.Controllers.HomeController.GetCustomers() in C:\Users\Manish Prahladka\Downloads\TelerikGridSample\TelerikGridSample\Sample.WebUI\Controllers\HomeController.cs:line 214
at Sample.WebUI.Controllers.HomeController.AjaxCustomersHierarchy() in C:\Users\Manish Prahladka\Downloads\TelerikGridSample\TelerikGridSample\Sample.WebUI\Controllers\HomeController.cs:line 28
at lambda_method(Closure , ControllerBase , Object[] )
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.c__DisplayClass15.b__12()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
InnerException: System.Data.SqlClient.SqlException
HResult=-2146232060
Message=A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 – Error Locating Server/Instance Specified)
Source=.Net SqlClient Data Provider
ErrorCode=-2146232060
Class=20
LineNumber=0
Number=-1
Server=””
State=0
StackTrace:
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean withFailover)
at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, SqlConnection owningObject, Boolean withFailover)
at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnection owningObject, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)
at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(SqlConnection owningObject, TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, SqlConnection owningObject, Boolean redirectedUserInstance)
at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection)
at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnection owningConnection, DbConnectionPool pool, DbConnectionOptions options, DbConnectionPoolKey poolKey)
at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject)
at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.Open()
at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
InnerException:
Hi Manish,
Do you have SQL Server 2008 R2 installed?
Good evening, I really enjoyed your post!
More I have a coding problem in time to display the error message to the user, I can’t find a solution.
Tested put in html and
in java script more still didn’t work always returns the messages with errors.
Know a workaround for this error?
Much thanks for the posts and for your attention!
…..Forgive me I forgot to remove the html tags in my question!
I Tested, to put meta charset = “utf-8” in html and
in java script more still didn’t work always returns the messages with errors.
I cant open the application.
“cannot be opened because it is version 661. This server supports version 655 and earlier. A downgrade path is not supported.\r\nCould not open new database ”
Sorry I’m new at this
Jangiksu,
It looks like you have SQL Server 2008 installed. The version of the database file is for SQL Server 2008 R2 or later. You’d need to install the upgrade.
thanks for your reply, im using sql server 2008 r2. still i cant open it.
I’m not sure, Jangiksu. That’s usually the cause. Maybe take a look at these pages for more ideas:
http://forums.asp.net/p/1560835/3859344.aspx
http://stackoverflow.com/questions/4257684/sql-server-attach-incorrent-version-661
Hi Harlan,
I wish I could try to help, but you’ve given me very little information to go by. Do you have more details on what you’re attempting?
Thanks.
Mark,
It is now working, I attached the database file using the management studio and created scripts and made it sure that its compatible to run on R2.
Thanks
Apparently, the client side validation is not working. it throws an error if I try to save a customers with no acctno, firstname and lastname. Not sure if this was the cause of the script I created previously
Jangisku, what browser are you using? I have a hunch it’s IE in compatibility mode. There’s an issue with client-side validation in earlier versions of IE. You can force it to IE 9 or higher by adding this to your layout file, in the <head> section:
<meta http-equiv=”X-UA-Compatible” content=”IE=EmulateIE9″ >
Yes it is IE9, the fix you sent is not working. I’ll try to investigate or use other browsers.
Thank you very much you’re very helpful
It is now working, thank you very much. Genius!!!!
Thanks. I should probably look into how to also get it to work with IE7 and IE8.
Yes please. I will wait for that. Thanks
Not exactly when I’ll be able to look at that, between work and moving, so don’t wait too long. But it’s on my list.
I downloaded the sample and run it with VWD 2010 Express. No data could be displayed. I made sure the data in customer table can be viewed in the designer. Are there some extra steps to take?
Hi Leo,
There aren’t any extra steps. Sometimes, the first time you run the app it can take several seconds (I’ve seen over 10 seconds at times) for the data to load, as SQL Server Express is starting up for the first time. Is the progress indicator in the lower left of the grid spinning when you run the app?
Hi Mark,
Thanks for replying! After raised the question yesterday, I continued to work on your sample project. Eventually, I got it work!
Here is what I have done:
Since I am using SQL Server 2008 R2 (not express edition), I needed to modify the server name. But more importantly, I had to change “user instance” from true to false. After that, everything went pretty smooth.
I really like how you make master/details fit toghter so well!
Thanks, Leo. Good information to know.
Firstly, great article explaining how to built using this control. Makes you wonder why Telerik can’t post something as comprehensive. I have my suspicions but that does into vendor lock-in scenarios 😉
You forgot to add [HttpPost] to your action method. The GridAction bypasses OnActionExecuted processing if it isn’t an AJAX method which should be the case for GET request. Allowing AJAX Get requests opens a site for AJAX hijacking and CSRF attacks.
But I can’t help but think that Telerik has again vastly overcomplicated what is essentially a bit of JSON processing. Why do you both the GridModel and an ActionFilter. Why do you need to return a View and have the GridAction perform black magic. Can’t they just create a GridJSON action result so the INTENT is clear.
I cannot imagine a valid scenario where you would want to return a View with the data if you have designed your system to be AJAX driven.
Mark
Love you did but when running your sample I’m getting the following error when it first loads.
{“Invalid object name ‘dbo.Customers’.”}
When it goes into the method.
private IEnumerable GetCustomers()
{
var viewModel = new CustomersViewModel();
var customers = from c in viewModel.Customers select c;
return customers.ToList();
}
Did change anything it happened from the first I loaded.
Was there an update I missed on your code base, ASPNETDB.mdf, or is this something different as it seems to be in the SampleModel entity but I’m a bit new to MVC and Entity framework.
Hi Mark,
I’d like to ask if it is possible to pass the orderBy and filterBy property of the telerik grid to the controller.
Thanks!
Hi Mark,
Hope you are doing good. Need your help again.
I have a situation where columns of MVC telerik grid are not visible on IE9(windows 7 macines) though its paging and the count of record which its shows is correct but it doest not show grid data at all where as same is working perfectly fine in IE7 on windows xp machines.
Also, if we see source through browsers view source then its show the data which is in tags.
So conclusion is data is rendering and getting binded but not seen on our browser.
Please help us if anything I am missing to make this work.
Thanks in advance.
Best regards,
Vinny
Hi, Vinny.
Did you try turning on compatibility mode in IE9 to see if it renders correctly then? Are you using the latest version of the library? I have not upgraded since I worked on my most recent project, but they may have made modifications to correct this under IE9, when IE9 was released. You can also force compatibility mode with a META tag: <meta http-equiv=”X-UA-Compatible” content=”IE=EmulateIE8″ /> This is what I had to do to make sure it worked under IE7 and IE8. IE9 had significant changes.
I’ve switched to KendoUI recently, since Telerik is phasing out the MVC extensions, and KendoUI effectively replaces it. KendoUI would better support IE9, but it’s not simply a matter of replacing the library. There are several differences, so replacing it would be a large effort.
Hi.
I guess my issue is easy to solve, but I can’t get to it.
In my model, I have an already filled IEnumerable of another class and all I want to do is a detailview containing that collection.
I mean, the data is already there, so I don’t need any ajax or similar databinding. Simple like that. Or not?
Thank you for the insightful tutorials.
Really the only decent resource on Telerik Grids that I have found.
I am modifying an MVC 3 solution that was created by someone else.
They have created the following in the View :
Html.Telerik().Grid()
… .OnDetailViewExpand(“ApplicationsGrid_onDetailViewExpand”))
Then there is a JS function like this :
ApplicationsGrid_onDetailViewExpand(e){
var name = e.masterRow.cells[1].innerText;
var appId = e.masterRow.cells[13].innerText;
var email = e.masterRow.cells[10].innerText;
var license = e.masterRow.cells[12].innerText;
…
}
These vars are used in calls to other functions. But most important is the appId which gets sent as a parameter in a jQuery.ajax call.
$.ajax({
url: “/Admin/DetailView”,
data: { id: appId },
success: //success
fail: //fail
type: “GET”
});
When I got an error of “Encountered a null property. method parameter cannot be null”.
Turns out the variables that pulled from the masterRow cells are null. Thus, no appId gets sent to the controller.
The “e” parameter for the onDetailViewExpand handler does not contain anything useful. However, the onComplete handler contains the data I need, but that only gets called on page load, right?
Any ideas?
thank you
Nevermind.
In the function ApplicationsGrid_onDetailViewExpand function,
var name = e.masterRow.cells[1].innerText;
var appId = e.masterRow.cells[13].innerText;
var email = e.masterRow.cells[10].innerText;
var license = e.masterRow.cells[12].innerText;
should be
var name = $(e.masterRow.cells[1]).text();
var appId = $(e.masterRow.cells[13]).text();
var email = $(e.masterRow.cells[10]).text();
var license = $(e.masterRow.cells[12]).text();
Maybe this will help some other poor soul struggling with Telerik.