KendoUI Grid - ForeignKey column not working in PopUp edit mode - kendo-ui

I've searched for all over the place (understatement) for a solution to my case to no avail until now. First, I'll explain my scenario:
I have an OpenAccess Model exposed as a WCF Data Service (oData v3);
I have an Kendo MVC Application;
I have a View with a grid, set for PopUp editing, AJAX Bound;
Before posting some code, let me explain my issue/difficulty. I have an entity with these properties:
TextoID
Titulo;
Corpo;
TipoTextoID;
TipoTexto;
There is a ForeignKey column set to the TipoTextoID property which get's correctly populated either in in-line or pop-up mode. But when it comes to changing data, it only works in-line mode. This is my issue I need it to work in a popup, since the "Corpo" property is bound to a KEndoUI Editor.
When in the popup, it does not show the correct value on the dropdown neither changes it when we select it.
Honestly I'm feeling stupid. I tried almost every sample, post, article I could find to no avail and I'm clueless.
I hope someone can help me on this. Thanks in advance to all!
So, here's the code.
The view:
#model IEnumerable<KendoMVC.CostSimulatorService.Texto>
#{
ViewBag.Title = "Textos";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Textos</h2>
#(Html.Kendo().Grid(Model) // Bind the grid to the Model property of the view
.Name("Grid")
.Columns(columns =>
{
columns.Bound(p => p.Titulo); //Create a column bound to the "ProductID" property
//columns.Bound(p => p.IsPrivado).ClientTemplate("<input type='checkbox' #= IsPrivado ? checked='checked': '' # class='chkbx' />"); //Create a column bound to the "ProductName" property
columns.Template(#<text></text>).ClientTemplate("<input type='checkbox' #= IsPrivado ? checked='checked': '' # class='chkbx' />"); //Create a column bound to the "ProductName" property
//columns.Bound(p => p.TiposTexto);
columns.ForeignKey(p => p.TipoTextoID,
(System.Collections.IEnumerable)ViewData["TiposTexto"],
"TipoTextoID",
"Designacao")
.Title("Tipo de texto").Width(150);
columns.Command(command =>
{
command.Edit();
command.Destroy();
}).Width(200);
})
.ToolBar(commands => commands.Create())
.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("Texto"))
.DataSource(dataSource => dataSource
.Ajax() //specify server type
.Model(model =>
{
model.Id(texto => texto.TextoID); // Specify the property which is the unique identifier of the model
model.Field(texto => texto.TextoID).Editable(false); // Make the ProductID property not editable
})
.Create(create => create.Action("CreateTexto", "BackOffice"))
.Read(read => read.Action("ReadTextos", "BackOffice"))
.Update(update => update.Action("UpdateTexto", "BackOffice"))
.Destroy(destroy => destroy.Action("DestroyTexto", "BackOffice")))
.Pageable() // Enable paging
.Sortable() // Enable sorting
.Selectable()
.Filterable()
.Scrollable()
)
<script type="text/javascript">
$(document).ready(function() {
$("form.k-edit-form").kendoValidator();
});
</script>
Next, then template:
#using System.Web.Mvc.Html;
#model KendoMVC.CostSimulatorService.Texto
Introduza o conteúdo que deseja
#Html.HiddenFor(model => model.TextoID)
<div id="divWrapper" style="width:99%; float:left;">
<div>
#Html.LabelFor(model => model.Titulo)
</div>
<div>
#Html.EditorFor(model => model.Titulo)
#Html.ValidationMessageFor(model => model.Titulo)
</div>
<div>
#Html.LabelFor(model => model.Corpo)
</div>
<div>
#(Html.Kendo().EditorFor(model => model.Corpo))
#Html.ValidationMessageFor(model => model.Corpo)
</div>
<div>
#Html.LabelFor(model => model.TipoTextoID)
</div>
<div>
#*#(Html.Kendo().DropDownListFor(model => model.TiposTexto))
#Html.ValidationMessageFor(model => model.TiposTexto)*#
#(Html.Kendo().DropDownListFor(m => m.TipoTextoID)
.Name("TiposTexto")
.DataTextField("Designacao")
.DataValueField("TipoTextoID")
.BindTo((System.Collections.IEnumerable)
ViewData["TiposTexto"]))
</div>
</div>
The controller:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Kendo.Mvc.Extensions;
using Kendo.Mvc.UI;
using KendoMVC.CostSimulatorService;
namespace KendoMVC.Controllers
{
public partial class BackOfficeController : Controller
{
#region CRUD
#region ReadTextos
public ActionResult ReadTextos([DataSourceRequest]DataSourceRequest request)
{
CostSimulatorModel modelo = new CostSimulatorModel(new Uri(#"http://localhost:53212/CostSimulatorModelService.svc/"));
IQueryable<Texto> textos = modelo.Textos;
DataSourceResult resultado = textos.ToDataSourceResult(request);
ViewData["Textos"] = textos;
return Json(resultado, JsonRequestBehavior.AllowGet);
}
#endregion
#region CreateTexto
public ActionResult CreateTexto([DataSourceRequest]DataSourceRequest request, Texto texto)
{
if (ModelState.IsValid)
{
CostSimulatorModel modelo = new CostSimulatorModel(new Uri(#"http://localhost:53212/CostSimulatorModelService.svc/"));
// Create a new Product entity and set its properties from the posted ProductViewModel
Texto entity = new Texto
{
TextoID = texto.TextoID,
Titulo = texto.Titulo,
Corpo = texto.Corpo,
IsPrivado = texto.IsPrivado,
TipoTextoID = texto.TipoTextoID,
TiposTexto = texto.TiposTexto
};
modelo.AddToTextos(entity);
// Insert the entity in the database
modelo.SaveChanges();
// Get the ProductID generated by the database
texto.TextoID = entity.TextoID;
}
// Return the inserted product. The grid needs the generated ProductID. Also return any validation errors.
return Json(new[] { texto }.ToDataSourceResult(request, ModelState));
}
#endregion
#region UpdateTexto
public ActionResult UpdateTexto([DataSourceRequest]DataSourceRequest request, Texto texto)
{
if (ModelState.IsValid)
{
CostSimulatorModel modelo = new CostSimulatorModel(new Uri(#"http://localhost:53212/CostSimulatorModelService.svc/"));
// Create a new Product entity and set its properties from the posted ProductViewModel
var entity = new Texto
{
TextoID = texto.TextoID,
Titulo = texto.Titulo,
Corpo = texto.Corpo,
IsPrivado = texto.IsPrivado,
TipoTextoID = texto.TipoTextoID,
TiposTexto = texto.TiposTexto
};
// Attach the entity
modelo.AttachTo("Textos", entity);
modelo.UpdateObject(entity);
// Update the entity in the database
modelo.SaveChanges();
}
// Return the updated product. Also return any validation errors.
return Json(new[] { texto }.ToDataSourceResult(request, ModelState));
}
#endregion
#region DestroyTexto
public ActionResult DestroyTexto([DataSourceRequest]DataSourceRequest request, Texto texto)
{
if (ModelState.IsValid)
{
CostSimulatorModel modelo = new CostSimulatorModel(new Uri(#"http://localhost:53212/CostSimulatorModelService.svc/"));
// Create a new Product entity and set its properties from the posted ProductViewModel
var entity = new Texto
{
TextoID = texto.TextoID
//Titulo = texto.Titulo,
//Corpo = texto.Corpo,
//IsPrivado = texto.IsPrivado,
//TipoTextoID = texto.TipoTextoID
};
// Attach the entity
modelo.AttachTo("Textos", entity);
// Delete the entity
modelo.DeleteObject(entity);
// Delete the entity in the database
modelo.SaveChanges();
}
// Return the removed product. Also return any validation errors.
return Json(new[] { texto }.ToDataSourceResult(request, ModelState));
}
#endregion
#endregion
}
}

I've finally got this sorted out with the precious help from KendoUI's premium forums.
So, to stop this from happening, one should use the default editor template for the ForeignKeyColumn as an editor for the "TipoTextoID", like so:
Model:
[UIHint("GridForeignKey")]
public int EmployeeID { get; set; }
Custom popup template:
#(Html.EditorFor(m => m.EmployeeID))
instead of using #(Html.Kendo().DropDownListFor(m => m.TipoTextoID)
Hope this may help others struggling with same thing.
All the best!

In addition to Stargazer's answer (Thanks!!!), below custom popup template (Views\Shared\EditorTemplates\GridForeignKey.cshtml) worked for me.
#model object
#(
Html.Kendo().DropDownListFor(m => m)
.BindTo((SelectList)ViewData[ViewData.TemplateInfo.GetFullHtmlFieldName("") + "_Data"])
)
Also, no need of specifying custom template so do below on grid view:
.Editable(editable => editable.Mode(Kendo.Mvc.UI.GridEditMode.PopUp))
Last clarification, add below to main grid model (not foreign key viewmodel) also name matches with custom template.
[UIHint("GridForeignKey")]

Related

How to load Dyanamic values to dropdown list in MVC6?

Here i am trying to create dynamic dropdown list which load items from the Viewmodel of View,
here is my model
I have created view model as below
passing View model to MVC View
public ActionResult Create()
{
OrgVM model = new OrgVM();
model.regions = getRegions();
return View(model);
}
private List<Region> getRegions()
{
var list = new List<Region>();
list = context.Set<Region>().ToList().Select(s => new Region
{
Id = s.RegionId,
Name = s.Name,
}).ToList();
return list;
}
now here in Create View am calling View model as below,
#model xyz.ViewModels.OrgVM
and am generating dropdown like
#Html.DropDownListFor(x => Model.regions, new SelectList(Model.regions), htmlAttributes: new { #class = "form-control" })
and it looks like below snapp, where it has to show dyanamic values in that dropdown list.
Resolved this with little R and D
#Html.DropDownListFor(m => Model.regions,Model.regions.Select(d => new
SelectListItem()
{
Value = d.RegionId.ToString(),
Text = d.Name.ToString()
}))

How do I correctly use EditorFor to display checkbox selections from an AJAX call, and POST the selections?

My question is two-fold.
I have a View that gets data on the change of a drop down selection.
The data retrieved is a List property of a ViewModel class using an Ajax call.
This data is shown as a selection of check boxes for the user to select any number of them.
If I return a Partial View from an AJAX call, this is easy enough, but from what I have experienced, this doesn't work for POST'ing back to the controller. Nothing is bound correctly.
From what I have read the correct way is to use EditorFor, So firstly, I cannot figure out how to populate the EditorFor from the AJAX call.
Secondly, If I test by sending initial data from my GET, the EditorFor displays the correct checkbox options, but when I POST, the count of the items is 0.
View:
#model EngineeringAssistantMVC.Controllers.FirmwareController.FirmwareViewModel
#using (Html.BeginForm("Upload", "Firmware", FormMethod.Post, new { #id = "uploadFirmwareForm", #class = "form-horizontal" }))
{
<!-- Device -->
<div class="form-group">
<div class="col-lg-1">
#Html.LabelFor(x => x.Device, htmlAttributes: new { #class = "control-label" })
</div>
<div class="col-lg-2">
#Html.DropDownListFor(x => x.Device, ViewBag.Devices as IEnumerable<SelectListItem>, new { #class = "form-control", #id = "Devices" })
</div>
<div class="col-lg-9">
#Html.ValidationMessageFor(x => x.Device, "", new { #class = "text-danger" })
</div>
</div>
#Html.EditorFor(x => x.SelectedModels, "SelectedModels", new { #id = "Models" })
#Html.HiddenFor(x => x.SelectedModels)
}
And the AJAX call:
function GetModels() {
$.ajax({
type: "GET",
url: '#Url.Action("GetModels", "Firmware", null)',
data: { SelectedDevice: $('#Devices').val() },
success: function (dataSet) {
//$('#Models').html(dataSet);
//$('#Models').data(dataSet);
//$('#Models').val(dataSet);
// How do I populate the EditorFor from the dataSet returned?
},
error: function (err) {
console.log("ERROR: " + err.responseText);
},
})
}
SelectedModels EditFor Template:
#model IEnumerable<EngineeringAssistantMVC.ViewModels.ModelViewModel>
#foreach (var item in Model)
{
#Html.CheckBoxFor(x => item.IsSelected)
#Html.Label(item.Description)
#Html.HiddenFor(x => item.ModelId)
#Html.HiddenFor(x => item.IsSelected)
#Html.HiddenFor(x => item.Description)
}
Controller:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase uploadFile, FirmwareViewModel firmwareViewModel)
{
// firmwareViewModel.SelectedModels count is 0 here
}
ModelFirmware Class:
public class ModelFirmware
{
public int ModelFirmwareId { get; set; }
public int FirmwareId { get; set; }
public int ModelId { get; set; }
}
FirmwareViewModel:
public class FirmwareViewModel
{
public int FirmwareViewModelId { get; set; }
[Required]
public string Device { get; set; }
public ICollection<ModelViewModel> SelectedModels { get; set; }
}
I just can't get it to work correctly.
EDIT 1: - Add method that returns the models
[HttpGet]
public ActionResult GetModels(string SelectedDevice)
{
var deviceAbbreviation = _dbContext.Radios.Where(x => x.RadioName == SelectedDevice).Select(x => x.ProjectAbbreviation).FirstOrDefault();
var models = _dbContext.AnatomyModels.Where(x => x.SerialPrefix.StartsWith(deviceAbbreviation.Trim()) && x.ParentId == 0).ToList();
List<ModelViewModel> mvms = models.Select(x => new ModelViewModel()
{
ModelId = x.AnatomyModelId,
Description = x.SerialPrefix,
IsSelected = false,
}).ToList();
return Json(mvms);
}
There are numerous issues with your code.
First your not using the EditorTemplate correctly. Change its name to ModelViewModel.cshtml to match the name of the class, and locate it in the /Views/Shared/EditorTemplates (or /Views/YourControllerName/EditorTemplates) folder. The template is then based on a single object (note also the LabelFor() required to create a label associated with the checkbox, and you need to delete the hidden input for IsSelected)
#model ModelViewModel
#Html.CheckBoxFor(m => m.IsSelected)
#Html.LabelFor(m => m.IsSelected, Model.Description)
#Html.HiddenFor(m => m.ModelId)
#Html.HiddenFor(m => m.Description)
Refer also Post an HTML Table to ADO.NET DataTable to understand why your foreach loop would never have created the correct name attributes for model binding.
Then in the main view use
<div id="container">
#Html.EditorFor(m => m.SelectedModels)
</div>
and remove the hidden input for SelectedModels (but before you do, inspect the html for that element to understand why its value would never bind). The EditorFor() method will correctly generate the html for each item in your collection.
Next, change your GetModels() method to return a partial view based on FirmwareViewModel since that is what you will be posting back. Note that you could return a JsonResult, but that would mean generating a whole lot of html in the ajax call back that would not be strongly typed.
[HttpGet]
public PartialViewResult GetModels(string SelectedDevice)
{
var deviceAbbreviation = _dbContext.Radios.Where(x => x.RadioName == SelectedDevice).Select(x => x.ProjectAbbreviation).FirstOrDefault();
var models = _dbContext.AnatomyModels.Where(x => x.SerialPrefix.StartsWith(deviceAbbreviation.Trim()) && x.ParentId == 0).ToList();
List<ModelViewModel> mvms = models.Select(x => new ModelViewModel()
{
ModelId = x.AnatomyModelId,
Description = x.SerialPrefix,
IsSelected = false, // not really necessary since its the default
}).ToList();
FirmwareViewModel model = new FirmwareViewModel
{
SelectedModels = mvms
};
return PartialView(model);
}
and your GetModels.cshtml view will be
#model FirmwareViewModel
#Html.EditorFor(m => m.SelectedModels)
Then, modify your ajax call to add the partial view in the success callback
$.ajax({
type: "GET",
url: '#Url.Action("GetModels", "Firmware")', // do not need to add 3rd parameter
data: { SelectedDevice: $('#Devices').val() },
success: function (response) {
$('#container').html(response);
},
error: function (err) {
console.log("ERROR: " + err.responseText);
},
})
The .html() function will replace any elements already existing in the <div id="container"> element
Finally, since your using a view model, make use of it and do not use ViewBag. Your view model should contain a IEnumerable<SelectListItem> Devices property which you populate in the GET method (and use #Html.DropDownListFor(x => x.Device, Model.Devices, new { #class = "form-control" }) in the view (note also that the method generates id="Device"). It should also contain a HttpPostedFileBase property to avoid the additional parameter in the POST method, and allow you to add validation attributes.

Kendo DropDownListFor() with ASP.NET-MVC

i have a problem into the ASP.NET-MVC Helper
I have a form that give a POST into action **create of the controller Occurrence passing a parameter of type occurrence that corresponding at the Model of the view where the form is inserted, for register the occurrence is needed an TypeOccurrenceID, i'm trying to get this value using Html.DropDownListFor(), but this not working when the form is posted, the Occurrence past in the parameter don't have
the OccurrenceTypeId corresponding with the OccurrenceType selected in the DropDownList
Someone had the same problem?
This is my Controller action
[HttpPost]
public ActionResult Create(Occurrence occurrence)
{
if (ModelState.IsValid)
{
try
{
db.Add<Occurrence>(occurrence);
return new HttpStatusCodeResult(200);
}
catch (Exception)
{
return new HttpStatusCodeResult(400);
}
}
return new HttpStatusCodeResult(400);
}
Here is my View
#using Common.Util
#using Common.Util.Configuration
#using CafData
#model Occurrence
<div class="box-form">
#using (Ajax.BeginForm("Create", "Occurrence",
new AjaxOptions
{
OnSuccess = "OnSuccess()",
OnFailure = "OnFailure()"
}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#*Area*#
<div class="row-fluid details-field">
#(Html.Kendo().DropDownList()
.Name("areas")
.HtmlAttributes(new { style = "width:300px" })
.OptionLabel("Selecione uma area...")
.DataTextField("Description")
.DataValueField("IdArea")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("readAreasForDropDown", "Area");
});
})
)
#*Occurrence type*#
#(Html.Kendo().DropDownListFor(m => m.OccurrenceTypeId)
.Name("occurrencetype")
.HtmlAttributes(new { style = "width:300px" })
.OptionLabel("Select a occurrence type...")
.DataTextField("Description")
.DataValueField("OccurrenceTypeId")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("lerOccurrenceTypeForDropDown",
"OccurrenceType").Data("filterArea").
Type(HttpVerbs.Post);
})
.ServerFiltering(true);
})
.Enable(false)
.AutoBind(false)
.CascadeFrom("areas")
)
<script>
function filterArea() {
return {
id: $("#areas").val()
};
}
</script>
<button class="k-button">Save</button>
}
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Sorry for the bad english!
The problem was the name of the dropdownlist, it must be the same name as the property of the model that you want bind.
Example:
#(Html.Kendo().DropDownListFor(m => m.OccurrenceTypeId)
.Name("OccurrenceTypeId")
Alternative:
The name property is not actually necessary when using DropDownListFor. So just removing this line would work as well:
.Name("occurrencetype")

DropDownListFor doesn't display selected item

I'm trying to get the drop down list to have my item selected when there is an item, but it never does. I've Googled this and tried many different methods, but they all seem to use a ViewModel containing the list instead of using ViewBag, but I would like to stick to the ViewBag if possible.
My controller:
[HttpGet]
public ActionResult Index(int? id)
{
ViewBag.SelectList = new SelectList(rep.GetItemList(), "id", "type");
if (id.HasValue)
{
var model = rep.GetItemByID(id.Value);
if ( model != null )
{
return View(model);
}
}
return View();
}
My View:
<div class="editor-field">
#Html.DropDownListFor(model => model.itemID, (SelectList)ViewBag.SelectList)
#Html.ValidationMessageFor(model => model.itemID)
</div>
This doesn't have my item selected in the DropDownList, and I've also tried having a list in the ViewBag and then constructing the SelectList in the View, which some posts say should solve the problem:
<div class="editor-field">
#Html.DropDownListFor(model => model.itemID, new SelectList(ViewBag.SelectList, "id", "type", Model.itemID))
#Html.ValidationMessageFor(model => model.itemID)
</div>
But none of it seems to work. So I was wondering if there is anyone where that is able to spot what I'm doing wrong?
make sure your itemID property is set in the model you are passing to the view
if (id.HasValue)
{
var model = rep.GetItemByID(id.Value);
model.itemID=id.Value;
return View(model);
}
I would try setting the selected value from the begining since SelectList is immutable.
[HttpGet]
public ActionResult Index(int? id)
{
if (id.HasValue)
{
ViewBag.SelectList = new SelectList(rep.GetItemList(), "id", "type", id );
var model = rep.GetItemByID(id.Value);
if ( model != null )
{
return View(model);
}
}
else
{
ViewBag.SelectList = new SelectList(rep.GetItemList(), "id", "type");
}
return View();
}
In your View use it like this:
#Html.DropDownListFor(model => model.itemID, (SelectList)ViewBag.SelectList, "Please select...")

How do I rebind my Telerik MVC grid after an item has been inserted

My Telerik MVC 3 grid is filled via Ajax. After inserting a row I need to rebind my grid. When I'm doing a rebind on the OnSave() event the data rebind is still sending on the Controller Action. I need something like an OnInserted event.
Any idea?
Use the following criteria,
#(Html.Telerik().Grid<PackageDetails>()
.Name("gvPackage")
.DataKeys(keys => keys.Add(k => k.PKG_CODE))
.Columns(column =>
{
column.Bound(c => c.PKG_NAME).Title("Description").Width(200);
column.Bound(c => c.MESG_UNIT).Title("Measuring Unit").Width(100);
column.Bound(c => c.STD_QNT).Title("Quantity").Width(100);
column.Bound(c => c.MODEL).Title("Model").Width(100);
column.Bound(c => c.COMP_CODE).ClientTemplate("<input type='text' id='txtSerial<#=COMP_CODE#>' value='<#=PKG_NAME#>' />").Title("Serial Number");
column.Bound(c => c.COMP_DESC).Title("Model").Width(100);
})
.DataBinding(dbBindings => dbBindings.Ajax().Select("_PackageDetailsLoad", "SalesDept"))
)
Controller Code
[GridAction]
public ActionResult _PackageDetailsLoad(string programID, string projectID, string packageID)
{
objLoginHelper = (LoginHelper)Session["LogInInformation"];
return View(new GridModel<PackageDetails>
{
Data = salesDal.ReadPackageDetails(programID, projectID, packageID)
});
}
In JavaScript use the following Code
$('#ddlProgram').change(function () {
LoadPackageAndBindGrid();
});
function LoadPackageAndBindGrid() {
var params = {
programID: $('#ddlProgram').val(),
projectID: $('#ddlProject').val(),
packageID: $('#ddlPackage').val()
};
var grid = $('#gvPackage').data('tGrid');
grid.dataSource._data = [];
// Reload The Package Details
grid.ajaxRequest(params);
}
You can rebind your grid by returning a GridModel from your controller after performing your insert code:
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult _ItemInsert(int id, MyObject obj)
{
//Rebind the grid by sending the GridModel back
return View(new GridModel(myData)); // where myData is your grid data
}
Don't forget to decorate your controller with [GridAction].
This assumes your grid has an ajax DataBinding declaration as follows:
dataBinding.Ajax()
.Insert("_ItemInsert", "Item" })
It depends on how you're adding the row. The grid should update automatically if you're doing in-grid editing. If you're adding a record from a form, you can use the client-side rebind() method to refresh the grid data.
http://www.telerik.com/help/aspnet-mvc/telerik-ui-components-grid-client-api-and-events.html#rebind
On Complete Of Your Action you can call:
jQuery("#gvPackage").data("t-grid").ajaxRequest()

Resources