I get this error in my PartialView when I run my project "An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in Unknown Module.
but was not handled in user code
Additional information: System.Web.Mvc.SelectListItem contains no definition for value"
I have two tables category and Product in my databse, using EntityFramework ADO.ENTITY and the project is ASP.NET MVC
In Product table I have ProductId, ProductName and CategoryId
I want to create Cascading from Category DropDownList to Product DropDownList , I mean I want first to chose Category and then all product with the same CategoryId.
But does not working as it is, and please Help me.
This is my Controller:
public class CasController : Controller
{
// GET: Cas
public ActionResult Index()
{
Db db = new Db();
ViewBag.Categories = new SelectList(GetCategoryList(), "CategoryId", "CategoryName");
return View();
}
public List<Category> GetCategoryList()
{
Db db = new Db();
List<Category> categories = db.Category.ToList();
return categories;
}
public ActionResult GetProductList(int catId)
{
using (Db db = new Db())
{
List<Product> prList = db.Product.Where(x => x.CatId == catId).ToList();
ViewBag.ProductOPtions = new SelectList(prList, "ProductId", "ProductName");
return PartialView("DisplayProduct");
}
}
}
And this is my Cascadingclass
public class CasCadingClass
{
public int CatId { get; set; }
public int PrId { get; set; }
}
This is Index view
#model Market.Models.CasCadingClass
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#if (ViewBag.Categories != null)
{
#Html.DropDownListFor(m => m.CatId, ViewBag.Categories as SelectList, "---Select Category---", new { #class = "form-control" })
}
#Html.DropDownListFor(m => m.PrId, new SelectList(""), "--Select Product--", new { #class = "form-control" })
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script>
$(document).ready(function () {
$("#CatId").change(function () {
var catId = $(this).val();
debugger
$.ajax({
type: "Post",
url: "/Cas/GetProductList?catId=" + catId,
contentType: "html",
success: function (response) {
debugger
$("#PrId").empty();
$("#PrId").append(response);
}
})
})
});
</script>
// Here in PartielView goes error
#model Market.Product
<option value="">--- select Product---</option>
#if (ViewBag.ProductOPtions != null)
{
foreach (var item in ViewBag.ProductOPtions)
{
<option value="#item.value">#item.Text</option>
}
}
Related
How is it possible that in a razor EditorTemplate the following commands generate a different value for the same ViewModel:
#Html.TextAreaFor(model => model.Value)
#Model.Value
And no, in the Value get property, the value is not changed
Update 1:
Sorry guys for the short message, you know, tired, frustrated...
Made a sample, got rid of all the partials and templates.
Give the textbox number 1, hit add, number 2, hit add, number 3, hit add.
The remove number 2.
The result is an out of sync between the textbox and the displayed value.
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
TestModel testModel = TestModel.Create();
Session["model"] = testModel;
return View("Index", testModel);
}
[HttpPost]
public ActionResult Submit(TestModel submitModel, string submit)
{
// merge values in current form
var testModel = Session["model"] as TestModel;
if (testModel == null) throw new Exception("No current model found.");
testModel.MergeFieldValues(submitModel);
if (submit.StartsWith("Add_"))
{
var rowGroupId = Guid.Parse(submit.Substring("Add_".Length));
TestRowGroup rowGroup = testModel.Groups.SelectMany(g => g.RowGroups).Single(rg => rg.RowGroupId == rowGroupId);
rowGroup.AddFieldRow();
}
if (submit.StartsWith("Del_"))
{
var fieldRowId = Guid.Parse(submit.Substring("Del_".Length));
testModel.RemoveFieldRow(fieldRowId);
}
return View("Index", testModel);
}
}
Model:
public class TestModel
{
public List<TestGroup> Groups { get; set; }
public static TestModel Create()
{
var testModel = new TestModel { Groups = new List<TestGroup>() };
var grp = new TestGroup { RowGroups = new List<TestRowGroup>() };
var rowGrp = new TestRowGroup { RowGroupId = Guid.NewGuid(), FieldRows = new List<TestFieldRow>() };
var fldRow = new TestFieldRow { FieldRowId = Guid.NewGuid(), Fields = new List<TestFormField>() };
var fld = new TestFormField { FieldId = Guid.NewGuid() };
fldRow.Fields.Add(fld);
rowGrp.FieldRows.Add(fldRow);
grp.RowGroups.Add(rowGrp);
testModel.Groups.Add(grp);
return testModel;
}
public void MergeFieldValues(TestModel src)
{
foreach (var srcField in src.Groups.SelectMany(g => g.RowGroups.SelectMany(rg => rg.FieldRows.SelectMany(fr => fr.Fields))))
{
var destField = Groups.SelectMany(g => g.RowGroups.SelectMany(rg => rg.FieldRows.SelectMany(fr => fr.Fields))).FirstOrDefault(f => f.FieldId == srcField.FieldId);
if (destField == null) throw new Exception("Field not found during merge");
destField.Value = srcField.Value;
}
}
public void RemoveFieldRow(Guid fieldRowId)
{
foreach (var group in Groups)
{
foreach (var rowGroup in group.RowGroups)
{
rowGroup.FieldRows.RemoveAll(fieldRow => fieldRow.FieldRowId == fieldRowId);
}
}
}
}
public class TestGroup
{
public List<TestRowGroup> RowGroups { get; set; }
}
public class TestRowGroup
{
public List<TestFieldRow> FieldRows { get; set; }
public Guid RowGroupId { get; set; }
public void AddFieldRow()
{
var newRow = new TestFieldRow
{
Fields = new List<TestFormField>()
};
newRow.FieldRowId = Guid.NewGuid();
var fld = new TestFormField { FieldId = Guid.NewGuid() };
newRow.Fields.Add(fld);
FieldRows.Add(newRow);
}
}
public class TestFieldRow
{
public Guid FieldRowId { get; set; }
public List<TestFormField> Fields { get; set; }
}
public class TestFormField
{
public Guid FieldId { get; set; }
public string Value { get; set; }
}
View:
#model FieldTest.Models.TestModel
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
#using (Html.BeginForm("Submit", "Home", FormMethod.Post))
{
for (int g = 0; g < Model.Groups.Count; g++)
{
for (int rg = 0; rg < Model.Groups[g].RowGroups.Count; rg++)
{
for (int fr = 0; fr < Model.Groups[g].RowGroups[rg].FieldRows.Count; fr++)
{
for (int f = 0; f < Model.Groups[g].RowGroups[rg].FieldRows[fr].Fields.Count; f++)
{
#Html.HiddenFor(model => model.Groups[g].RowGroups[rg].FieldRows[fr].Fields[f].FieldId)
#Model.Groups[g].RowGroups[rg].FieldRows[fr].Fields[f].Value
#Html.TextBoxFor(model => model.Groups[g].RowGroups[rg].FieldRows[fr].Fields[f].Value)
<button onclick="return confirm('Are you sure you would like to remove this row?');" type="submit" value="#string.Format("Del_{0}", Model.Groups[g].RowGroups[rg].FieldRows[fr].FieldRowId)" name="submit">Remove</button>
<hr />
}
}
<button type="submit" value="#string.Format("Add_{0}", Model.Groups[g].RowGroups[rg].RowGroupId)" name="submit">Add</button>
}
}
<input type="submit" value="Submit" name="submit" />
}
</body>
</html>
More importantly, are you sure this is an EditorTemplate problem? If you put the code in your main view, does it also happen? Did you try that? Or did you assume it was an EditorTemplate problem?
Since you neglected to provide any context for your question, all we can do is guess. More than likely, you have modified the contents of the view model in a post operation, and are now surprised that the Html helpers are using the old value rather than the value from the model.
If so, this is "by design", and a well documented (hundreds if not thousands of questions already here on SO about this issue). The MVC Helpers prefer the contents of the ModelState over the model itself in post operations. You have to clear the ModelState in order to work around it.
This is how we implement cascade drop down for year and month. After selecting year/month the form is submitted. When the View back after Submit, the selected Year value is persist (as we have handling for this in Model), but the selected Month value is not persist. What need to do to persist the value?
Model
public class MyViewModel
{
public int? Year { get; set; }
public int? Month { get; set; }
public IEnumerable<SelectListItem> Years
{
get
{
return Enumerable.Range(2000, 12).Select(x => new SelectListItem
{
Value = x.ToString(),
Text = x.ToString()
});
}
}
}
Controller
The HttpGet and HttpPost action,
//
// GET: /MenuSix/
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
public ActionResult Months(int year)
{
if (year == 2011)
{
return Json(
Enumerable.Range(1, 3).Select(x => new { value = x, text = x }),
JsonRequestBehavior.AllowGet
);
}
return Json(
Enumerable.Range(1, 12).Select(x => new { value = x, text = x }),
JsonRequestBehavior.AllowGet
);
}
//
// POST: /MenuSix/
[HttpPost]
public ActionResult Index(MyViewModel myViewModel)
{
var month = myViewModel.Month; //11
var year = myViewModel.Year; //2011
return View(myViewModel);
}
View
What need to change here to persist month value "Enumerable.Empty()"?
#model DemoWeb.Models.MenuSix.MyViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.Year, new SelectList(Model.Years, "Value", "Text"), "-- select year --")
#Html.DropDownListFor(x => x.Month, Enumerable.Empty<SelectListItem>(), "-- select month --")
<div align="center">
<input type="submit" id="valSubmit" value="Save"/>
</div>
}
#section PageScriptsAndCSS{
<script type="text/javascript">
$(document).ready(function () {
$('#Year').change(function () {
debugger;
var selectedYear = $(this).val();
if (selectedYear != null && selectedYear != '') {
$.getJSON('#Url.Action("Months")', { year: selectedYear }, function (months) {
var monthsSelect = $('#Month');
monthsSelect.empty();
$.each(months, function (index, month) {
monthsSelect.append($('<option/>', {
value: month.value,
text: month.text
}));
});
});
}
});
});
</script>
}
Yes, you need to change the way you bind month's dropdown. As, you bind it with an empty list
You would not get the selected month value after form submit. I have changed your code in my way. let me know if it fits to your need.
Model
public class MyViewModel
{
public int? Year { get; set; }
public int? Month { get; set; }
public static IEnumerable<int> GetMonthsRangeOf(int year) {
return (year ==2011) ? Enumerable.Range(1, 3) : Enumerable.Range(1, 12);
}
public IEnumerable<SelectListItem> Years { get {
return Enumerable.Range(2000, 12).Select(x => new SelectListItem
{
Value = x.ToString(),
Text = x.ToString()
});
} }
public IEnumerable<SelectListItem> Months
{
get
{
IEnumerable<SelectListItem> months = null;
if (! this.Year.HasValue)
{
months = Enumerable.Empty<SelectListItem>();
}
else
{
months = GetMonthsRangeOf(this.Year.Value).Select(x => new SelectListItem
{
Value = x.ToString(),
Text = x.ToString()
});
}
return months;
}
}
}
Controller
public ActionResult Months(int year)
{
return Json(
MyViewModel.GetMonthsRangeOf(year).Select(x => new { value = x, text = x }),
JsonRequestBehavior.AllowGet
);
}
//
// POST: /MenuSix/
[HttpPost]
public ActionResult About(MyViewModel myViewModel)
{
var month = myViewModel.Month; //11
var year = myViewModel.Year; //2011
return View(myViewModel);
}
View
#model TestFreezeMVC.ViewModel.MyViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.Year, new SelectList(Model.Years, "Value", "Text"), "-- select year --")
#Html.DropDownListFor(x => x.Month, new SelectList(Model.Months, "Value", "Text"), "-- select month --");
<div align="center">
<input type="submit" id="valSubmit" value="Save"/>
</div>
}
<script type="text/javascript">
$(document).ready(function () {
$('#Year').change(function () {
var selectedYear = $(this).val();
if (selectedYear != null && selectedYear != '') {
$.getJSON('#Url.Action("Months")', { year: selectedYear }, function (months) {
var monthsSelect = $('#Month');
monthsSelect.empty();
$.each(months, function (index, month) {
monthsSelect.append($('<option/>', {
value: month.value,
text: month.text
}));
});
});
}
});
});
</script>
Suggest if I did something wrong..
Below all my code,
*Model *
Below is Model code,
public class MyViewModel
{
public int? Year { get; set; }
public int? Month { get; set; }
public IEnumerable<SelectListItem> Years
{
get
{
return Enumerable.Range(2000, 12).Select(x => new SelectListItem
{
Value = x.ToString(),
Text = x.ToString()
});
}
}
}
Controller
Below is Controller code,
//
// GET: /MenuSix/
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
public ActionResult Months(int year)
{
if (year == 2011)
{
return Json(
Enumerable.Range(1, 3).Select(x => new { value = x, text = x }),
JsonRequestBehavior.AllowGet
);
}
return Json(
Enumerable.Range(1, 12).Select(x => new { value = x, text = x }),
JsonRequestBehavior.AllowGet
);
}
View
Below is View code,
#model DemoWeb.Models.MenuSix.MyViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(
x => x.Year,
new SelectList(Model.Years, "Value", "Text"),
"-- select year --"
)
#Html.DropDownListFor(
x => x.Month,
Enumerable.Empty<SelectListItem>(),
"-- select month --"
)
}
#section PageScriptsAndCSS{
<script type="text/javascript">
$('#Year').change(function () {
debugger;
var selectedYear = $(this).val();
if (selectedYear != null && selectedYear != '') {
$.getJSON('#Url.Action("Months")', { year: selectedYear },
function (months) {
var monthsSelect = $('#Month');
monthsSelect.empty();
$.each(months, function (index, month) {
monthsSelect.append($('<option/>', {
value: month.value,
text: month.text
}));
});
});
}
});
</script>
}
I'm testing above code, but in jquery code not called here, please suggest why the dropdown change event not called in jquery?
Wrap the javascript code in document.ready to ensure that control is available when binding then event. IT looks this javascript is rendered at the head and at that point drop down is not yet added to the DOM
$(document).ready(function()
{
$("#year").///rest of the code
});
I am trying to create an Ajax Telerik grid in Razor that has an updateable foreign key column that shows a dropdown list. I've copied my page pretty much like the example, and everything works. I can add new records, delete them and edit them. The only thing that doesn't work is that I get a textfield with the integer when I update a record in my grid, instead of a dropdown list with all the possibilities of the foreign key table.
Anyone have any ideas on how I could fix this? See code below.
Telerik grid:
#(Html.Telerik().Grid<EditableAccount>()
.Name("Grid")
.ToolBar(commands => commands.Insert().ButtonType(GridButtonType.Text).ImageHtmlAttributes(new { style = "margin-left:0" }))
.DataBinding(dataBinding => dataBinding.Ajax()
.Insert("InsertAccount", "Administration")
.Update("SaveAccount", "Administration")
.Delete("DeleteAccount", "Administration"))
.DataKeys(keys => { keys.Add(a => a.AccountId); })
.Columns(columns =>
{
columns.ForeignKey(b => b.BankId, (IEnumerable)ViewData["Banks"], "ID", "Name").Width(50);
columns.Bound(a => a.AccountNumber).Width(110);
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.Image);
commands.Delete().ButtonType(GridButtonType.Image);
}).Width(16);
})
.Editable(editing => editing.Mode(GridEditMode.InLine))
.Pageable()
.Scrollable()
.Sortable()
)
Controller:
[GridAction]
public ActionResult Accounts()
{
ViewData["Banks"] = db.Banks.Select(b => new { Id = b.BankId, Name = b.Name });
return View(new GridModel(accountRepository.All()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult InsertAccount()
{
//Create a new instance of the EditableProduct class.
EditableAccount account = new EditableAccount();
//Perform model binding (fill the product properties and validate it).
if (TryUpdateModel(account))
{
//The model is valid - insert the product.
accountRepository.Insert(account);
}
//Rebind the grid
return View(new GridModel(accountRepository.All()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult SaveAccount(int id, int bankId)
{
EditableAccount account = new EditableAccount
{
AccountId = id,
Bank = db.Banks
.Where(b => b.BankId == bankId)
.Select(b => b.Name).SingleOrDefault(),
BankId = bankId
};
TryUpdateModel(account);
accountRepository.Update(account);
return View(new GridModel(accountRepository.All()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult DeleteAccount(int id)
{
//Find a customer with ProductID equal to the id action parameter
EditableAccount account = accountRepository.One(a => a.AccountId == id);
if (account != null)
{
//Delete the record
accountRepository.Delete(account);
}
//Rebind the grid
return View(new GridModel(accountRepository.All()));
}
Model:
public class EditableAccount
{
[ScaffoldColumn(false)]
public int AccountId { get; set; }
[Required]
[UIHint("GridForeignKey")]
[DisplayName("Bank")]
public int BankId { get; set; }
public string Bank { get; set; }
[Required]
[DisplayName("AccountNumber")]
public int AccountNumber { get; set; }
}
Repository:
public IList<EditableAccount> All()
{
IList<EditableAccount> result =
(from account in db.Accounts
select new EditableAccount
{
AccountId = account.AccountId,
Bank = account.Bank.Name,
BankId = account.BankId,
AccountNumber = account.AccountNr
}).ToList();
return result;
}
public EditableAccount One(Func<EditableAccount, bool> predicate)
{
return All().Where(predicate).FirstOrDefault();
}
public void Insert(EditableAccount insertedAccount)
{
Account account = new Account();
account.BankId = insertedAccount.BankId;
account.AccountNr = insertedAccount.AccountNumber;
db.Accounts.InsertOnSubmit(account);
db.SubmitChanges();
}
public void Update(EditableAccount updatedAccount)
{
Account account = db.Accounts.SingleOrDefault(a => a.AccountId == updatedAccount.AccountId);
account.BankId = updatedAccount.BankId;
account.AccountNr = updatedAccount.AccountNumber;
db.SubmitChanges();
}
public void Delete(EditableAccount deletedAccount)
{
Account account = db.Accounts.SingleOrDefault(a => a.AccountId == deletedAccount.AccountId);
db.Accounts.DeleteOnSubmit(account);
db.SubmitChanges();
}
Someone answered my question on the Telerik forums:
http://www.telerik.com/community/forums/aspnet-ajax/grid/asp-net-mvc-razor-grid-with-editable-foreign-key-dropdown-column.aspx
I'm trying to initialize my checkbox in controller like the code below, but in the view it's not selected whether it's true or false
controller :
foreach (var item in AssignedUsers)
{
if (dc.App_UserTasks.Any(u => u.UserId == item.UserId && u.TaskId == ProjectTask.Id))
{
Users.Single(u => u.Id == item.Id).IsChecked = true;
}
else
{
Users.Single(u => u.Id == item.Id).IsChecked = false;
}
}
view:
#for (int i = 0; i < Model.Responsibles.Count; i++)
{
#Html.CheckBoxFor(u => u.Responsibles[i].IsChecked)
}
send model from controller to view :
var EPT = new EditProjectTaskModel
{
ProjectId = ProjectTask.ProjectId,
Title = ProjectTask.Title,
ProjectName = ProjectTask.App_Project.ProjectName,
Id = ProjectTask.Id,
Description = ProjectTask.Description,
EstimatedTime = ProjectTask.EstimatedTime,
Status = ProjectTask.Status,
Responsibles = Users.ToList()
};
return PartialView("_EditProjectTask", EPT);
Assuming your User ViewModel looks like this
public class UserViewModel
{
public string Name { set;get;}
public int UserId { set;get;}
public bool IsSelected { set;get;}
}
And you have your main view model has a collection of this UserViewModel
public class EditProjectTaskModel
{
public List<UserViewModel > Responsibles { set; get; }
public EditProjectTaskModel()
{
if(this.Responsibles ==null)
this.Responsibles =new List<UserViewModel >();
}
}
Create an editor template called Responsibles.cshtml with the below content
#model YourNameSpace.UserViewModel
#Html.CheckBoxFor(x => x.IsSelected)
#Html.LabelFor(x => x.IsSelected, Model.Name)
#Html.HiddenFor(x => x.UserId)
Now include that in your main view like this, instead of the loop
#model EditProjectTaskModel
#using (Html.BeginForm())
{
//other elements
#Html.EditorFor(m=>m.Responsibles)
<input type="submit" value="Save" />
}
If you want to get the selected checkboxes on a form submit.
[HttpPost]
public ActionResult Save(EditProjectTaskModel model)
{
List<int> userIDs=new List<int>();
foreach (UserViewModel user in model.Responsibles)
{
if (user.IsSelected)
{
//you can get the selected user id's here
userIDs.Add(user.UserId);
}
}
}