I am trying to create a master/detail Telerik MVC grid with Ajax binding. When I select a detail row to update, click "Edit", edit the value, then click "Update", the value is not updated on the client side. I see in the debugger that the model is not updated when I call TryUpdateModel. Here is the view:
#(Html.Telerik().Grid<FeeSrcMstrDteViewModel>()
.Name("FeeSourceConfirmMasterGrid")
.Columns(columns =>
{
columns.Bound(m => m.FEE_SRC_NME).Width(65).Title(#FeeBuilderResource.FeeSourceName);
columns.Bound(m => m.PUBLISHING_ENTITY_NME).Width(45).Title(#FeeBuilderResource.PublishingEntity);
columns.Bound(m => m.FEE_SRC_SRVC_CATEGORY_NME).Width(55).Title(#FeeBuilderResource.ServiceTypeCategory);
columns.Bound(m => m.FEE_SRC_PUBLISHED_DTE).Width(45).Title(#FeeBuilderResource.PublishedDate).Format("{0:d}");
columns.Bound(m => m.YEAR).Width(30).Title(#FeeBuilderResource.Year);
if (Model.FeeSourceMasterDate.GEO_LOC_ID == 1 || Model.FeeSourceMasterDate.GEO_LOC_ID == 4)
{
columns.Bound(m => m.STATE).Width(50).Title("State or carrier locality");
}
else
{
columns.Bound(m => m.CARRLOC).Width(50).Title("State or carrier locality");
}
//columns.Command(commands => commands.Custom("ExportToExcel").Text("Export to Excel")
//.DataRouteValues(route => route.Add(m => m.FeeSourceMasterDate.FEE_SRC_MSTR_ID).RouteKey("FEE_SRC_MSTR_ID"))
//.Ajax(false).Action("_ConfirmMasterExportXlsAjax", "FeeSrcFlatRate", new { feeSourceServiceMasterDateId = "<#= FEE_SRC_MSTR_ID #>" }))
//.HtmlAttributes(new { style = "text-align: center" }).Width(50).Title(#FeeBuilderResource.Actions);
})
//.ClientEvents(events => events.OnSave("FeeSourceTestGrid_OnSave"))
.DetailView(details => details.ClientTemplate(
Html.Telerik().Grid<FeeSourceFlatRateCommentsViewModel>()
.Name("FeeSourceHome_<#= FEE_SRC_MSTR_ID #>")
.DataKeys(keys => keys.Add(m => m.CommentId))
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_GetFeeSourceDetailDataAjax", "FeeSrcFlatRate")
.Update("_UpdateFeeSourceDetailDataAjax", "FeeSrcFlatRate")
)
.Columns(columns =>
{
columns.Bound(m => m.CommentId).Width(200).Hidden();
columns.Bound(m => m.FeeSourceDescription).Width(200).Title("Fee source description");
columns.Bound(m => m.FeeSourceComments).Width(200).Title("Fee source comments");
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.ImageAndText);
}).Width(75);
})
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_GetFeeSourceDetailDataAjax", "FeeSrcFlatRate", new { id = "<#= FEE_SRC_DTE_ID #>" }))
.Editable(editing => editing.Mode(GridEditMode.InLine).InsertRowPosition(GridInsertRowPosition.Top))
.Footer(false)
.ToHtmlString()
))
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_GetFeeSourceDataAjax", "FeeSrcFlatRate"))
.Pageable(paging => paging.PageSize(20))
//.Scrollable(scrolling => scrolling.Height(580))
.Sortable()
)
The select methods work great, but not the update method. I would expect the model to be updated when i call TryUpdateModel in my controller, but I do not see that. Here is my controller code:
[HttpPost]
[GridAction]
public ActionResult _UpdateFeeSourceDetailDataAjax(int id)
{
FeeSourceFlatRateViewModel modelTest = new FeeSourceFlatRateViewModel();
List<FeeSourceFlatRateCommentsViewModel> commentsList = new
List<FeeSourceFlatRateCommentsViewModel>();
// get the model by id
IFeeSourceMstrRepository repo = new FeeSourceMstrRepository();
IList<FeeSrcMstrDteViewModel> modelTestList = repo.GetFeeSrcMstrDteViewModel((int)id);
FeeSrcMstrDteViewModel modelTestChange = modelTestList[0];
//Perform model binding (fill the product properties and validate it).
if (TryUpdateModel(modelTestChange))
{
var testy = modelTestChange;
// set the comments
FeeSourceFlatRateCommentsViewModel modelComments = new FeeSourceFlatRateCommentsViewModel
{
CommentId = id,
FeeSourceDescription = modelTestChange.FEE_SRC_DESC,
FeeSourceComments = modelTestChange.FEE_SRC_COMMENTS
};
commentsList.Add(modelTestChange);
// save the object
repo.SaveFeeSrcUOW(saveModel);
OperationStatus opStatus = repo.Save();
}
TempData["FeeSourceFlatRateModel"] = modelTestChange;
return View(new GridModel(commentsList));
}
What am I missing? Thanks for the help!
I got some help from the Telerik site. As it turns out, I was trying to update the incorrect model. My master grid is bound to FeeSrcMstrDteViewModel, but my detail grid, which I want to update, is bound to FeeSourceFlatRateCommentsViewModel.
The fix was to call TryUpdateModel on the FeeSourceFlatRateCommentsViewModel.
Related
I have the following Kendo().Grid():
#(Html.Kendo().Grid<TicketReportPropertyEntity>()
.Name("TicketReportPropertyGrid")
.Columns(columns =>
{
columns.Bound(c => c.ID).Hidden();
columns.Bound(c => c.PropertyName).Title("Property Name").EditorTemplateName("_PropertyNameEditor").Width(900);
columns.Bound(c => c.Amount).Title("Amount").Format("{0:C}").Width(90);
columns.Command(command => command.Destroy()).Width(150);
})
.Events(events => events.DataBound("Databound").SaveChanges("SaveGrid").Edit("Edit"))
.ToolBar(toolbar =>
{
toolbar.Create();
toolbar.Save();
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Navigatable()
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.ServerOperation(false)
//.Events(events => events.Error("error_handler"))
.Model(model =>
{
model.Id(c => c.ID);
model.Field(c => c.PropertyName);
model.Field(c => c.Amount);
})
.Create(create => create.Action("AddTicketReportProperty", "TicketReportProperty").Data("GetData"))
.Read(read => read.Action("GetData", "TicketReportProperty", Model))
.Update(update => update.Action("UpdateTicketReportProperty", "TicketReportProperty"))
.Destroy(delete => delete.Action("DeleteTicketReportProperty", "TicketReportProperty"))
)
)
And my controller's method:
[HttpPost]
public ActionResult DeleteTicketReportProperty([DataSourceRequest] DataSourceRequest request, TicketReportPropertyModel model)
{
var result = new TicketReportPropertyModel().DeleteTicketReportProperty(model.ID);
return Json(new[] { model }.ToDataSourceResult(request, ModelState));
}
Here is a "SaveGrid" function:
function SaveGrid(e) {
console.log("save")
var rowsCount = e.sender.dataSource.data().length;
var totalSum = 0;
if (rowsCount > 0) {
for (var i = 0; i < rowsCount; i++) {
totalSum += e.sender.dataSource.data()[i].Amount;
}
}
var ticketAmount = $('#Ticket_Amount').val();
console.log(ticketAmount);
if (totalSum != ticketAmount) {
console.log("failed");
//show the popup
e.preventDefault();
}
}
It should add, update and delete records. Now, I'm working on deleting the record. But the event does not call the controller.
I'm following telerik example here
What am I missing?
I believe you are not following exactly the example from telerik. When you create the Grid you're using an Entity TicketReportPropertyEntity, but in the Controller you are receiving a Model TicketReportPropertyModel.
Make sure you create your Grid using the Model, not the Entity, as the example you're following.
#(Html.Kendo().Grid<TicketReportPropertyModel>()
Can someone please help with my question:
The edit command in kendo grid isn't reaching my controller.
Am I missing something?
#(Html.Kendo().Grid<WEEKLY_ORDERS_LINES>()
.Name("orderDetails_edit" + Model.OrderID)
.Columns(columns =>
{
columns.Bound(e => e.ID).Hidden(true);
columns.Bound(e => e.INGRED_NAME).Title("Ingredient Name").Width(120).HeaderHtmlAttributes(new { style = "text-align: center;" }).HtmlAttributes(new { style = "text-align: center;" });
columns.Command(command => { command.Edit(); }).Width(60);
})
.Editable(e => e.Mode(GridEditMode.InLine))
.DataSource(dataSource => dataSource
.Ajax()
.Sort(sort => sort.Add("INGRED_NAME").Ascending())
.Model(model =>
{
model.Id(p => p.ID);
model.Field(p => p.ID).DefaultValue(new Guid());
model.Field(f => f.INGRED_NAME).Editable(true);
})
.Update(update => update.Action("Update", "Food"))
.Read(read => read.Action("Read", "Food").Data("additionalInfo"))
)
.Events(events => events.Cancel("refreshView"))
)
And my controller is like this:
public ActionResult Update([DataSourceRequest] DataSourceRequest request, WEEKLY_ORDERS_LINES model)
{
if (model != null && ModelState.IsValid)
{
WEEKLY_FOOD dbFood = _db.WEEKLY_FOOD.Find(model.ID);
dbFood.INGRED_NAME = model.INGRED_NAME;
_db.SaveChanges();
}
ActionResult a = Json(new[] { model }.ToDataSourceResult(request, ModelState));
return a;
}
I'm a colleague of Rute.
The problem that was ocurring was that the kendo Update was calling other controller and not the right one. This was happening because all the results of the data that were being rendered didn't have an unique identifier.
This unique identifier is now assign to the model.Id and the problem is solved. I understood this after seeing this link http://www.telerik.com/forums/wrong-methods-are-fired.
Thanks for the help.
App type: ASP.NET MVC with Kendo framework
Problem Encountered: The detail template is not firing the controller action method. Please find the attached screenshot also. I checked it under the firebug window also, there is no ajax call to the controller action method "PublishedImportFiles_Read". This is making me crazy and annoying. No error is thrown or shown for this anomaly. I guess i am missing something very small.
Please help.
MVC - View Code
<div class="gapLeft gapBelow20" style="width:70%;">
#(Html.Kendo().Grid<ViewModels.PublishedImportViewModel>()
.Name("GridImport")
.Columns(columns =>
{
columns.Bound(p => p.TemplateGroup).Title("Template Group").Width("120px");
columns.Bound(p => p.TaxYear).Title("Tax Year").Width("70px");
columns.Bound(p => p.FileDescription).Title("Description").Width("200px");
columns.Bound(p => p.DatePublished).Title("Published Date").Format("{0:MM/dd/yyyy}").Width("100px");
columns.Bound(p => p.PublishedBy).Title("Published By").Width("100px");
})
.Scrollable()
.ClientDetailTemplateId("tplGridImport")
.Events(et => et.DataBound(fnCall.Replace("functionName", "gridImpDataBound")))
.Events(et => et.DetailInit(fnCall.Replace("functionName", "gridImpChildInit")))
.HtmlAttributes(new { style = "height:300px;" })
.DataSource(ds => ds.Ajax().ServerOperation(false)
.Read(read => { read.Action("PublishedImport_Read", "Import"); })
.Model(m => { m.Id(p => p.TemplateGroupID); })
)
)
</div>
<script id="tplGridImport" type="text/kendo-tmpl">
#(Html.Kendo().Grid<ViewModels.PublishedImportViewModel>()
.Name("GridImport_#=TemplateGroupID#")
.Columns(columns =>
{
columns.Bound(c => c.TemplateGroup).Title("Template Group").Width("120px");
columns.Bound(c => c.TaxYear).Title("Tax Year").Width("70px");
})
.DataSource(dsx => dsx.Ajax()
.Read(rd => { rd.Action("PublishedImportFiles_Read", "Import"); })
)
.ToClientTemplate()
)
</script>
Import Controller ActionResult Method:
[OutputCache(Duration = 0)]
public ActionResult PublishedImportFiles_Read([DataSourceRequest] DataSourceRequest request)
{
int groupID = 5;
IEnumerable<PublishedImportViewModel> pubVM = Enumerable.Empty<PublishedImportViewModel>();
var pubRecords = base.ScenarioMangtService.GetPublishedImportFilesByTemplateGroup(ClientID, SelectedYear, groupID);
pubVM = pubRecords.Select(s => new PublishedImportViewModel
{
ImportFileJobID = s.ImportFileJobID,
TemplateGroupID = s.TemplateGroupID,
TemplateGroup = s.TemplateGroup,
FileName = s.FileName,
FileDescription = s.FileDescription,
TaxYear = SelectedYear,
DatePublished = s.DatePublished,
PublishedBy = s.PublishedBy
});
return Json(pubVM.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
There was nothing wrong with the KendoGrid code. Strangely, there was a javascript error in another js file. And for some weird reason, it was breaking the binding of the detail template grid.
So when i commented the other broken code in another file, this grid starts working automatically.
I am trying to bind a Kendo Grid in Asp.Net MVC but the paging doesn't work. The PageSize and the Total records are correct, the problem is that it isn't possible to navigate to the next page. All the buttons are displayed but they are disabled.
The view's code is:
<% Html.Kendo().Grid(Model)
.Name("PartListGrid")
.Columns(columns =>
{
columns.Bound(p => p.Id).Title("Id").Visible (false);
columns.Bound(p => p.Quantity).Title("Quantity")).Width(130);
columns.Bound(p => p.PartNumber).Title("Part Number").Width(130);
})
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(p=>p.Id))
.PageSize(5)
.ServerOperation(false)
)
.Pageable()
.Render();
%>
The Controller's code :
public ActionResult GetPartListInfo(string id)
{
List<PartListViewModel> partList = new List<PartListViewModel>();
XrmServiceContext xrmServiceContext = new XrmServiceContext();
f1_workorder workOrder = xrmServiceContext.f1_workorderSet.Where(i => i.Id == new Guid(workOrderId)).FirstOrDefault();
PartListViewModel partViewModel = null;
foreach (f1_workorderproduct workorderproduct in xrmServiceContext.f1_workorderproductSet.Where(i => i.f1_WorkOrder.Id == workOrder.Id).ToList())
{
Product product = xrmServiceContext.ProductSet.Where(j => j.Id == workorderproduct.f1_Product.Id).FirstOrDefault();
partViewModel = new PartListViewModel();
partViewModel.Id = workorderproduct.f1_Product.Id.ToString();
partViewModel.Quantity = workorderproduct.f1_EstimateQuantity.GetValueOrDefault(0);
partViewModel.PartNumber = workorderproduct.f1_name;
partList.Add(partViewModel);
}
return View("PartList",partList);
}
Any advice is appreciated!
Thank you very much for your help!
Mimi
I bet you have to throw in a data source read configuration so that the dataset can fetch the data on paging when needed.
.DataSource(dataSource => dataSource
...
.Read(read => read.Action("YourAction", "YourController))
...
From the samples and documentation, it looks like if I want to update one row (or do a batch update) in a Telerik grid, I also need to refresh the data in the entire grid.
Is there a way to just update that one row on the client and not return all the grid data every time I want to do an update? It seems extremely wasteful to keep returning that much data every time.
Even if I do a batch update, why do I need to return all the new data?
Generally a database has many users, and your user interface may be accessed by multiple people. If the grid is going to update, then it will include any other changes that may have happened to the data since the grid originally loaded.
The grid allows you to limit the number of records that are shown at a time via the paging option. On our project we are loading the grid with AJAX, and using IQueryable's in the controller. When we call ToDataSourceResult(request) on the IQueryable, it only retrieves the data that is needed, and does not load the full table from the database.
On the Razor View:
#(Html.Kendo().Grid<WebApp.ViewModels.AccountViewModel>()
.Name("Accounts")
.Columns(columns =>
{
columns.Bound(c => c.AccountNumber);
columns.Bound(c => c.Name);
columns.Bound(c => c.Address1);
columns.Bound(c => c.Address2);
columns.Bound(c => c.City);
columns.Bound(c => c.State);
columns.Bound(c => c.Zip);
columns.Bound(c => c.PrimaryContact).Title("Contact");
})
.Filterable(filterable => filterable
.Extra(false)
.Operators(operators => operators
.ForString(str => str.Clear()
.Contains("Contains")
.DoesNotContain("Doesn't Contain")
)
)
)
.Groupable()
.Sortable()
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(new int[] { 5, 10, 20, 50, 100, 500 })
.ButtonCount(5)
)
.DataSource(dataSource => dataSource
.Ajax()
.Sort(sort => sort.Add("Name").Ascending())
.PageSize(20)
.Read(read => read.Action("Account_Read", "Account"))
)
)
Then in the controller:
public ActionResult Account_Read([DataSourceRequest] DataSourceRequest request)
{
var jsonlist = GetAccounts()
.Select(sa => new AccountViewModel
{
ID = sa.ID,
AccountNumber = sa.AccountNumber,
Name = sa.Name,
Address1 = sa.Address1,
Address2 = sa.Address2,
City = sa.City,
State = sa.State,
Zip = sa.Zip,
PrimaryContact = sa.User.FirstName + " " + sa.User.LastName
}
).ToDataSourceResult(request);
return Json(jsonlist);
}
UPDATE
We have an inline edit form on one of our Kendo grids. It took us a bit to get it how we watned. In the Razor View:
#(Html.Kendo().Grid<DataViewModel>()
.Name("Data")
.Columns(columns =>
{
columns.Bound(c => c.FieldName)
.EditorTemplateName("DataFieldName")
.Title("Item Name")
.Width(200);
columns.Bound(c => c.TextValue)
.Title("Text");
columns.Command(command => { command.Edit(); command.Destroy(); })
.Title("Actions")
.Width(172);
})
.Filterable()
.Sortable()
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(new int[] { 5, 10, 20, 50, 100 })
.ButtonCount(5)
)
.ToolBar(toolbar => toolbar.Create())
.Editable(editable => editable.Mode(GridEditMode.InLine))
.DataSource(dataSource => dataSource
.Ajax()
.Sort(sort => sort.Add("CreatedDate").Descending())
.PageSize(20)
.Events(events => events.Error("error_handler"))
.Model(model =>
{
model.Id(pd => pd.ID);
model.Field(pd => pd.CreatedDate).Editable(false);
model.Field(pd => pd.ValuesID).DefaultValue(Model.Values.ID);
})
.Read(read => read.Action("Data_Read", "History", new { ValuesID = Model.Values.ID }))
.Create(update => update.Action("Data_Create", "History"))
.Update(update => update.Action("Data_Update", "History"))
.Destroy(update => update.Action("Data_Destroy", "History"))
)
)
<script type="text/javascript">
function error_handler(e) {
if (e.errors) {
var message = "Errors:\n";
$.each(e.errors, function (key, value) {
if ('errors' in value) {
$.each(value.errors, function () {
message += this + "\n";
});
}
});
alert(message);
}
}
</script>
In order to use the Kendo date time picker for the date option, we had to create a custom editor template. We created a new folder under Views >> Shared >> EditorTemplates. The file name is DataFieldName.cshtml:
#model WebApp.ViewModels.DataViewModel
#using WebApp.ViewModels
#(Html.Kendo().ComboBox()
.Name("FieldName")
.Placeholder("Select/Create Item Name")
.DataTextField("Name")
.DataValueField("Name")
.Filter(FilterType.Contains)
.DataSource(source => source
.Read(read => read.Action("GetFieldNames", "History"))
)
)
Then the controller has all of the action results for the AJAX calls:
[HttpPost]
public ActionResult Data_Create(DataViewModel DataIn)
{
var Data = new Data();
Data.ValuesID = DataIn.ValuesID;
Data.FieldName = DataIn.FieldName;
Data.TextValue = DataIn.TextValue;
if (DataIn.TimeValue != null) Data.TimeValue = DataIn.TimeValue;
if (ModelState.IsValid)
{
db.Datas.Add(Data);
db.SaveChanges();
}
return RedirectToAction("EditValue", new
{
ValueID = DataIn.ValuesID,
tankID = db.Values.FirstOrDefault(pv => pv.ID == DataIn.ValuesID).TankID
});
}
[HttpPost]
public ActionResult Data_Update(DataViewModel DataIn)
{
var Data = db.Datas.Find(DataIn.ID);
Data.FieldName = DataIn.FieldName;
Data.TextValue = DataIn.TextValue;
if (DataIn.TimeValue != null) Data.TimeValue = DataIn.TimeValue;
if (ModelState.IsValid)
{
db.Entry(Data).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("EditValue", new
{
ValueID = DataIn.ValuesID,
tkID = db.Values.FirstOrDefault(pv => pv.ID == DataIn.ValuesID).tkID
});
}
[HttpPost]
public ActionResult Data_Destroy(DataViewModel DataIn)
{
var Data = db.Datas.Find(DataIn.ID);
db.Datas.Remove(Data);
db.SaveChanges();
return RedirectToAction("EditValue", new
{
ValueID = DataIn.ValuesID,
tkID = db.Values.FirstOrDefault(pv => pv.ID == DataIn.ValuesID).tkID
});
}
public ActionResult GetFieldNames()
{
var jsonList = db.Datas
.Select(pd => new
{
Name = pd.FieldName
})
.Distinct()
.OrderBy(pd => pd.Name)
.ToList();
return Json(jsonList, JsonRequestBehavior.AllowGet);
}