Nested EditorTemplates in Telerik MVC Grid Edit Popup Window Not Displaying in Custom Edit Template - asp.net-mvc-3

I have a grid using AJAX DATABINDING.
When I issue a POPUP EDITING command with TEMPLATENAME SPECIFIED my NESTED EDITOR TEMPLATES are not populating.
My Models
namespace eGate.BackOffice.WebClient.Model
{
public class TemplateTesterModel
{
public int TemplateModelId { get; set; }
public string TemplateModelName { get; set; }
public List<UserRole> UserRoles { get; set; }
}
}
{
public class TemplateTesterModels : List<TemplateTesterModel>
{
}
}
My View
#model eGate.BackOffice.WebClient.Model.TemplateTesterModels
#Html.EditorFor(m=>m)
#( Html.Telerik().Grid<eGate.BackOffice.WebClient.Model.TemplateTesterModel>()
.Name("Grid")
.DataKeys(keys => { keys.Add(m=>m.TemplateModelId); })
.Columns(columns =>
{
columns.Bound(o => o.TemplateModelId);
columns.Bound(o => o.TemplateModelName).Width(200);
columns.Bound(o => o.UserRoles).ClientTemplate(
"<# for (var i = 0; i < UserRoles.length; i++) {" +
"#> <#= UserRoles[i].RoleName #> <#" +
"} #>")
;
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.Text);
}).Width(180).Title("Commands");
})
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_SelectAjaxEditing", "TemplateTester")
.Insert("_InsertAjaxEditing", "Grid")
.Update("_SaveAjaxEditing", "TemplateTester")
.Delete("_DeleteAjaxEditing", "TemplateTester")
)
.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("TemplateTesterModel"))
)
My Controller
namespace eGate.BackOffice.WebClient.Controllers
{
public class TemplateTesterController : Controller
{
public ActionResult Index()
{
return View(GetTemplateTesters());
//return View(new GridModel(GetTemplateTesters()));
}
private TemplateTesterModels GetTemplateTesters() {
TemplateTesterModels returnme = new TemplateTesterModels();
returnme.Add(new TemplateTesterModel());
returnme[0].TemplateModelId = 0;
returnme[0].TemplateModelName = "Template Tester 0";
returnme[0].UserRoles = new List<UserRole>();
returnme[0].UserRoles.Add(new UserRole() { RoleName = "Role1", IsChecked = true, Description = "Role for 1" });
returnme[0].UserRoles.Add(new UserRole() { RoleName = "Role2", IsChecked = false, Description = "Role for 2" });
returnme[0].UserRoles.Add(new UserRole() { RoleName = "Role3", IsChecked = false, Description = "Role for 3" });
return returnme;
}
[GridAction]
public ActionResult _SelectAjaxEditing()
{
return View(new GridModel(GetTemplateTesters()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult _SaveAjaxEditing(int id)
{
return View(new GridModel(GetTemplateTesters()));
}
[GridAction]
public ActionResult _InsertAjaxEditing(){
return View(new GridModel(GetTemplateTesters()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult _DeleteAjaxEditing(int id)
{
return View(new GridModel(GetTemplateTesters()));
}
}
}
My EditorTemplates
Shared/EditorTemplates/TemplateTesterModel.cshtml
#model eGate.BackOffice.WebClient.Model.TemplateTesterModel
<div>TemplateTesterModel Editor</div>
<div>#Html.EditorFor(m=>m.TemplateModelId)</div>
<div>#Html.EditorFor(m=>m.TemplateModelName)</div>
<div>Roles</div>
<div>#Html.EditorFor(m=>m.UserRoles)</div>
Shared/EditorTemplates/UserRole.cshtml
#model eGate.BackOffice.WebClient.Model.UserRole
<div>
I can has user role?
#Html.CheckBoxFor(m=>m.IsChecked)
</div>
This renders out as such:
As you can see the #Html.EditFor statements that precede the grid filter down through to the userrole EditorTemplate as expected. Additionally we can see that role data is in the grid because it is showing up in the role column.
But click the edit window and this is the result:
As you can see the UserRoles template is not populating with the roles on the UserRoles property of the TemplateTesterModel we're editing.
Am I missing something? Why is the .UserRoles property not populating in the telerik grid pop-up window?

This could be a "by design" decision of ASP.NET MVC. It does not automatically render display and editor templates for nested complex objects. I even have a blog post discussing this.
Long story short you need to create a custom editor template for the parent model.

Related

Bind local data to kendo grid and make it sortable

I am trying to setup kendo mvc ui grid using local data and make it sortable as in this demo Binding to local data.
However grid doesn't show data and if I hook up the onDataBound event, data inside there is undefined.
If I comment out DataSource setup on view, data gets populated at first, but disappears after sorting performed on any column.
I am using Kendo UI version: "2013.3.1119", asp.net mvc 4.
Please advice.
Thanks.
view
#model TestViewModel
#(Html.Kendo().Grid(Model.TestList)
.Name("grid2")
.Columns(columns =>
{
columns.Bound( p => p.Id ).Title( "ID" );
columns.Bound( p => p.Name ).Title("Product Name");
})
.Pageable()
.Sortable()
.Scrollable(scr=>scr.Height(430))
.Filterable()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.ServerOperation(false))
)
controller and model
public class TestController : Controller
{
//
// GET: /Test/
public ActionResult Index()
{
var mdl = new TestViewModel();
mdl.TestList = Test().ToList();
return View(mdl);
}
public IEnumerable<TestMe> Test()
{
var lst = new List<TestMe>();
for( int i = 0; i < 5; i++ )
{
lst.Add( new TestMe
{
Id = i,
Name = i.ToString(),
} );
}
return lst;
}
}
public class TestViewModel
{
public List<TestMe> TestList { get; set; }
public TestViewModel()
{
TestList = new List<TestMe>();
}
}
public class TestMe
{
public int Id { get; set; }
public string Name { get; set; }
}

MVC 3 Model binding and No parameterless constructor defined for this object

I created a view that was working wonderfully until I added some JQuery to support cascading drop downs. I believe in doing that, I broke the binding between the view and the model. I'm getting the error "No parameterless constructor defined for this object." when the form is submitted. The obvious solution would be to add a parameterless constructor, but I'm assuming that the postmodel will be null? Code Snippets below.
Thanks in Advance for your help.
View:
<script type="text/javascript">
$(document).ready(function () {
$("#ddlCategories").change(function () {
var iCategoryId = $(this).val();
$.getJSON(
"#Url.Content("~/Remote/SubCategoriesByCateogry")",
{ id: iCategoryId },
function (data) {
var select = ResetAndReturnSubCategoryDDL();
$.each(data, function (index, itemData) {
select.append($('<option/>', { value: itemData.Value, text: itemData.Text }));
});
});
});
function ResetAndReturnSubCategoryDDL() {
var select = $('#ddlSubCategory');
select.empty();
select.append($('<option/>', { value: '', text: "--Select SubCategory--" }));
return select;
}
});
...
<div class="editor-field">
#Html.DropDownList("iCategoryID", Model.Categories,"--Select Category--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlCategories"}})
#Html.ValidationMessage("iCategoryID")
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubCategories, "SubCategory")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SubCategories, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new { id = "ddlSubCategory" })
#Html.ValidationMessage("iSubCategoryID")
</div>
Controller:
[HttpPost]
public ActionResult Create(VendorCreateModel postModel)
{
VendorCreateEditPostValidator createValidator = new VendorCreateEditPostValidator(
postModel.iCategoryID,
postModel.iSubCategoryID,
postModel.AppliedPrograms,
m_unitOfWork.ProgramRepository,
new ModelStateValidationWrapper(ModelState));
if (ModelState.IsValid)
{
int categoryId = int.Parse(postModel.iCategoryID);
int subcategoryId = int.Parse(postModel.iSubCategoryID);
var programIds = postModel.AppliedPrograms.Select(ap => int.Parse(ap));
var programs = m_unitOfWork.ProgramRepository.GetPrograms(programIds);
Vendor vendor = postModel.Vendor;
vendor.Category = m_unitOfWork.CategoryRepository.GetCategory(categoryId);
vendor.SubCategory = m_unitOfWork.SubCategoryRepository.GetSubCategory(subcategoryId);
foreach (Program p in programs)
vendor.Programs.Add(p);
m_unitOfWork.VendorRepository.Add(vendor);
m_unitOfWork.SaveChanges();
return RedirectToAction("Index");
}
VendorCreateModel model = new VendorCreateModel(
postModel.Vendor,
postModel.iCategoryID,
postModel.iSubCategoryID,
postModel.AppliedPrograms,
User.Identity.Name,
m_unitOfWork.CategoryRepository,
m_unitOfWork.SubCategoryRepository,
m_unitOfWork.PermissionRepository);
return View(model);
}
RemoteController:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult SubCategoriesByCateogry(int id)
{
System.Diagnostics.Debug.WriteLine(id);
var SubCategories = db.SubCategories
.Where(v => v.iCategoryID == id)
.OrderBy(v => v.sDesc)
.ToList();
var modelData = SubCategories.Select(v => new SelectListItem()
{
Text = v.sDesc,
Value = v.iSubCategoryID.ToString()
});
return Json(modelData, JsonRequestBehavior.AllowGet);
}
VendorCreateModel:
public class VendorCreateModel
{
public VendorCreateModel()
{
}
public VendorCreateModel(
Vendor vendor,
string categoryId,
string subcategoryId,
IEnumerable<string> appliedPrograms,
string username,
ICategoryRepository categoryRepository,
ISubCategoryRepository subcategoryRepository,
IPermissionRepository permissionRepository)
{
UserHasProgramsValidator programValidator = new UserHasProgramsValidator(username, permissionRepository);
var availablePrograms = programValidator.AvailablePrograms;
HashSet<Category> applicableCategories = new HashSet<Category>();
foreach (var p in availablePrograms)
foreach (var c in categoryRepository.GetCategoriesByProgram(p.iProgramID))
applicableCategories.Add(c);
this.Vendor = vendor;
this.AppliedPrograms = appliedPrograms;
this.Categories = new SelectList(applicableCategories.OrderBy(x => x.sDesc).ToList(), "iCategoryID", "sDesc");
this.SubCategories = new SelectList(subcategoryRepository.GetAllSubCategories().OrderBy(x => x.sDesc).ToList(), "iSubCategoryID", "sDesc");
if (!string.IsNullOrEmpty(categoryId))
{
int temp;
if (!int.TryParse(categoryId, out temp))
throw new ApplicationException("Invalid Category Identifier.");
}
this.iCategoryID = categoryId;
this.iSubCategoryID = subcategoryId;
this.ProgramItems = availablePrograms
.Select(p => new SelectListItem()
{
Text = p.sDesc,
Value = p.iProgramID.ToString(),
Selected = (AppliedPrograms != null ? AppliedPrograms.Contains(p.iProgramID.ToString()) : false)
});
}
public Vendor Vendor { get; set; }
public SelectList Categories { get; set; }
public SelectList SubCategories { get; set; }
public string iCategoryID { get; set; }
public string iSubCategoryID { get; set; }
public IEnumerable<SelectListItem> ProgramItems { get; set; }
[AtLeastOneElementExists(ErrorMessage = "Please select at least one program.")]
public IEnumerable<string> AppliedPrograms { get; set; }
}
I correct the issue and wanted to share in case someone else was banging their head against their desk like Ihave been. Basically I changed the dropdownlistfor to reflect:
#Html.DropDownListFor(model => model.iSubCategoryID, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlSubCategory"},{"name","iSubCategoryID"}})
Assuming here the problem is in your VendorCreateModel, you either need to add a parameterless constructor or remove it, and create an instance in your action method and populate it by TryUpdateModel. Or parse the form using FormsCollection (not a fan).
You don't have the code for your viewmodel posted here but the basic assumption is that it will map.

Multiple roles in Asp.net MVC3

I have an admin site for the admin to create users. Here he has to chose the roles for the user - like the Asp.NET configuration site. I made 3 checkboxes with different roles.
[Authorize(Roles = "Admin")]
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
var Rolemodel = model.RolesContainer;
// Attempt to register the user
MembershipCreateStatus createStatus;
Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
[Authorize(Roles = "Admin")]
public ActionResult Register()
{
List<SelectListItem> tempRoles = new List<SelectListItem>();
tempRoles.Add(new SelectListItem{ Text = "Admin", Selected = false, Value = "Admin" });
tempRoles.Add(new SelectListItem{ Text = "Production", Selected = false, Value = "Production"});
tempRoles.Add(new SelectListItem{ Text = "Sale", Selected = false, Value = "Sale"});
return View(new RegisterModel { RolesContainer = tempRoles });
}
----View--------
#{ foreach (var item in Model.RolesContainer)
{
#Html.DisplayFor(m => item.Text)
#Html.CheckBoxFor(m => item.Selected)
}
}
When I check them and submit, I get to the breakpoint in my Register action, but the RolesContainer is null at this point - can anyone tell me why this is?
I copied and pasted your code (except the cshtml) into the following files and it works as expected.
HomeController.cs
using System.Collections.Generic;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
List<SelectListItem> tempRoles = new List<SelectListItem>();
tempRoles.Add(new SelectListItem { Text = "Admin",
Selected = false,
Value = "Admin" });
tempRoles.Add(new SelectListItem { Text = "Production",
Selected = false,
Value = "Production" });
tempRoles.Add(new SelectListItem { Text = "Sale",
Selected = false,
Value = "Sale" });
return View(new RegisterModel { RolesContainer = tempRoles });
}
}
}
RegisterModel
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace MvcApplication1.Models
{
public class RegisterModel
{
[Required]
[Display(Name = "Brugernavn")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email adresse")]
public string Email { get; set; }
public List<SelectListItem> RolesContainer { get; set;}
}
}
Home/Index.cshtml
#model MvcApplication1.Models.RegisterModel
#{
foreach (var item in Model.RolesContainer)
{
#Html.DisplayFor(m => item.Text)
#Html.CheckBoxFor(m => item.Selected)
}
}
Most likely there is something wrong in your Register.cshtml file, of which we don't have to validate.
Currently when you are assigning the value to RolesContainer it has not been initialised. You simply need to initialise it in the constructor of your class:
public class RegisterModel
{
public RegisterModel() {
RolesContainer = new List<SelectListItem>;
}
// rest of your code
}
Alternatively you could create a constructor which accepts the List<> as a parameter, but the above method will work for your current code structure.
If I understand well (may be I'm totally wrong), the model you post to httppost register (RegisterModel) doesn't contains the RolesContainer you expect. I think, asp.net mvc is not able to match multiple checkboxes with a List<SelectListItem>, a ListBox should be better

MVC3 Can't get model back to the controller

I have the following ViewModel
public class RecommendationModel
{
public List<CheckBoxItem> CheckBoxList { get; set; }
}
public class CheckBoxItem
{
public string Text { get; set; }
public bool Checked { get; set; }
public string Link { get; set; }
}
With the following View
model Sem_App.Models.RecommendationModel
#using (Html.BeginForm())
{
for (int i = 0; i < Model.CheckBoxList.Count(); i++) {
#Html.CheckBoxFor(m => m.CheckBoxList[i].Checked)
#Html.DisplayFor(m => m.CheckBoxList[i].Text)
}
<input type="submit" value="Add To Playlist" />
}
With the following controller actions
//get
public ActionResult Recommendation()
{
RecommendationModel model = new RecommendationModel();
model.CheckBoxList = new List<CheckBoxItem>();
return PartialView(model);
}
//post
[HttpPost]
public ActionResult Recommendation(RecommendationModel model)
{
foreach (var item in model.CheckBoxList)
{
if (item.Checked)
{
// do something with item.Text
}
}
}
Problem is whenever I select some items and press the submit button the model returned has CheckBoxList as empty. How can I change my view to return the list of CheckBoxList? Trying
#Html.HiddenFor(m => m.checkBoxList) did not work for me
I think you nee something like this
#using (Html.BeginForm())
{
for (int i = 0; i < Model.CheckBoxList.Count(); i++) {
#Html.CheckBoxFor("Model.CheckBoxItem[" + i + "].Checked" , m => m.CheckBoxList[i].Checked)
#Html.DisplayFor("Model.CheckBoxItem[" + i + "].Text",m => m.CheckBoxList[i].Text)
}
Try adding the link as a hidden field: #Html.HiddenFor(m => m.CheckBoxList[i].Link )
Check how the checkbox input name is in the rendered html and also check the form action is sending to the corrent controller/action and the method is post
it should be something very simple.. create the action param as array with the same name of the "name" attribute form check box input
something like this
[HttpPost]
public ActionResult Delete(int[] checkName)
{
}

Error when trying to post MVC form with Dropdown list in it

I looked at similar posts but nothing working for my case.
I have a form which loads fine and I see the categories dropdown with all categories in it.
The problem is when I try to post the form.
I get this error:
The ViewData item that has the key 'Category' is of type 'System.String' but must be of type 'IEnumerable'.
#Html.DropDownList("Category", Model.Categories) <-- red color
Here is my view:
#using (Html.BeginForm("Save", "Album", FormMethod.Post, new { id = "frmNewAlbum" }))
{
#Html.DropDownList("Category", Model.Categories)
}
Here is my model:
public class AlbumModel
{
public string Title { get; set; }
public string Category { get; set; }
public List<SelectListItem> Categories { get; set; } <-- holds categories
}
This is the controller actions to view the page:
[HttpGet]
public ActionResult Save()
{
var model = new AlbumModel();
var categories = new List<SelectListItem>() { new SelectListItem() { Text = "-- pick --" } };
categories.AddRange(svc.GetAll().Select(x => new SelectListItem() { Text = x.Name, Value = x.Name }));
model.Categories = categories;
return View(model);
}
Action that receives the post:
[HttpPost]
public ActionResult Save(AlbumModel model)
{
var album = new AlbumDoc()
{
Category = model.Category,
Title = model.Title,
};
svc.SaveAlbum(album);
return View(model);
}
In your POST action you seem to be redisplaying the same view but you are not populating the Categories property on your view model which will contain the dropdown list values. And by the way I would recommend you using strongly typed helper. So:
public class AlbumController: Controller
{
[HttpGet]
public ActionResult Save()
{
var model = new AlbumModel();
model.Categories = GetCategories();
return View(model);
}
[HttpPost]
public ActionResult Save(AlbumModel model)
{
var album = new AlbumDoc()
{
Category = model.Category,
Title = model.Title,
};
svc.SaveAlbum(album);
model.Categories = GetCategories();
return View(model);
}
private IList<SelectListItem> GetCategories()
{
return svc
.GetAll()
.ToList()
.Select(x => new SelectListItem
{
Text = x.Name,
Value = x.Name
});
}
}
and in your view:
#model AlbumModel
...
#using (Html.BeginForm("Save", "Album", FormMethod.Post, new { id = "frmNewAlbum" }))
{
#Html.DropDownListFor(
x => x.Category,
Model.Categories,
-- pick --
)
}

Resources