Answered by:
DropDownListFor - How to bind to Create post parameter in controller?

Question
-
User-1521109604 posted
Edit - So if I use index 0, then I'm able to see the model in the Create post method. That just leaves me to add multiple tags, I'm guessing with the use of a 'Add' button and then you would submit/post.
@Html.DropDownListFor(model => model.allTags[0].TagId, new SelectList(Model.allTags, "TagId", "Name"), "Select Tag", new { @class = "form-control" })
When I debug and view the model parameter for the Create [HttpPost] method, data is stored in Recipe, but nothing is stored in allTags. Strangely, if I don't choose from the Drop Down List then count = 1, otherwise its 0.
The Create form
Extra:
In addition to this, I would like to add multiple Tags to a Recipe, hence would need an 'Add Tag' button instead of relying on a single Submit button where it would only allow for one tag to be paired with a Recipe. How would I go about this?
The Create View:@model MVCApp.ViewModels.CreateRecipe @{ ViewBag.Title = "Create"; } <h2>Create</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Create Recipe</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.DropDownListFor(model => model.allTags, new SelectList(Model.allTags, "TagId", "Name"), "Select Tag", new { @class = "form-control" }) </div> <div class="form-group"> @Html.LabelFor(model => model.Recipe.Name, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Recipe.Name, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Recipe.Name, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Recipe.Instructions, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Recipe.Instructions, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Recipe.Instructions, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "ViewRecipes") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
The controller Create Post method:
// POST: Recipe/Create [HttpPost] public ActionResult Create(CreateRecipe model) { try { // TODO: Add insert logic here return RedirectToAction("ViewRecipes"); } catch { return View(); } }
The CreateRecipe ViewModel (uses Tag and Recipe Classes):
public class CreateRecipe { // A list of all the tags available public List<Tag> allTags { get; set; } // A Recipe object public Recipe Recipe { get; set; } }
The Tag Class:
public class Tag { public int TagId { get; set; } [Required] [StringLength(50, ErrorMessage = "Tag cannot be longer than 50 characters.")] [Display(Name = "Tag Name:")] [RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$", ErrorMessage = "Must start with a capital letter, only alphabetic characters and no spaces allowed.")] public string Name { get; set; } }
The Recipe Class:
public class Recipe { public int RecipeId { get; set; } public string Name { get; set; } public string Instructions { get; set; } }
Friday, September 18, 2020 2:55 AM
Answers
-
User-1521109604 posted
Thanks @bruce (sqlwork.com) for the insight.
Thanks @YihuiSun I used your checkbox solution for the customer side.
I found out my mistakes. I didn't actually set up the SelectList properly in my Model class. I also switched to a ListboxFor. The following solution:
The ViewModel:public class CreateRecipe { // A Recipe object public Recipe Recipe { get; set; } [Required(ErrorMessage = "At least one Tag is required.")] [Display(Name = "Select multiple tags:")] public int[] SelectedTagIds { get; set; }
public IEnumerable<SelectListItem> TagList { get; set; } }The Controller:
// GET: Recipe/Create public ActionResult Create() { ViewBag.Message = "Create Recipe"; var createRecipe = new CreateRecipe { Recipe = new Recipe(), SelectedTagIds = new[] { 2 }, TagList = GetAllTagTypes() }; return View(createRecipe); }
// POST: Recipe/Create [HttpPost] public ActionResult Create(CreateRecipe model) { try { // TODO: Add insert logic here return RedirectToAction("ViewRecipes"); } catch { return View(); } }
public List<SelectListItem> GetAllTagTypes() { List<SelectListItem> items = new List<SelectListItem>(); var data = TagProcessor.LoadTags(); foreach (var row in data) { items.Add(new SelectListItem { Text = row.Name, Value = row.TagId.ToString() }); }
return items; }The View:
<div class="col-md-3"> @Html.ListBoxFor(model => model.SelectedTagIds, Model.TagList, new { style = "width:200px;height:300px; padding:5px;", @onchange = "getSelectedTags(this)" }) @Html.ValidationMessageFor(model => model.SelectedTagIds, "", new { @class = "text-danger" }) </div>
Resources:
https://www.completecsharptutorial.com/asp-net-mvc5/html-listboxfor-and-html-listboxforfor-example-in-asp-net-mvc.php- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, September 25, 2020 11:00 PM
All replies
-
User-474980206 posted
The select only posts the selected value, not the option list. Your create model should have property for this. You should add a int TagId, and use this in the select.
@Html.DropDownListFor(model => model.TagId, new SelectList(Model.allTags, "TagId", "Name"), "Select Tag", new { @class = "form-control" })
Friday, September 18, 2020 2:28 PM -
User1686398519 posted
Hi James_00068,
According to your needs, I suggest you use CheckBox. I modified it according to your code, you can refer to it.
Tag
public class Tag { public int TagId { get; set; } [Required] [StringLength(50, ErrorMessage = "Tag cannot be longer than 50 characters.")] [Display(Name = "Tag Name:")] [RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$", ErrorMessage = "Must start with a capital letter, only alphabetic characters and no spaces allowed.")] public string Name { get; set; } public bool isActive { get; set; } }
RecipeController
public ActionResult Create() { CreateRecipe model = new CreateRecipe(); model.allTags = new List<Tag> (); for (int i = 1; i < 5; i++) { model.allTags.Add(new Tag {TagId=i,Name="Tag"+i.ToString(),isActive=false }); } return View(model); } [HttpPost] public ActionResult Create(CreateRecipe model) { return View(model); }
Create
<div class="form-group"> <ul class="list-group col-md-10 col-md-offset-1"> <li class="list-group-item active"> All tags </li> <li class="list-group-item"> @for (var i = 0; i < Model.allTags.Count(); i++) { <label> @Html.HiddenFor(model => Model.allTags[i].TagId)@Html.HiddenFor(model => Model.allTags[i].Name) @Html.CheckBoxFor(model => Model.allTags[i].isActive)@Html.DisplayFor(model => Model.allTags[i].Name) </label> } </li> </ul> </div>
Here is the result.
Best Regards,
YihuiSun
Tuesday, September 22, 2020 5:41 AM -
User-1521109604 posted
Thanks @bruce (sqlwork.com) for the insight.
Thanks @YihuiSun I used your checkbox solution for the customer side.
I found out my mistakes. I didn't actually set up the SelectList properly in my Model class. I also switched to a ListboxFor. The following solution:
The ViewModel:public class CreateRecipe { // A Recipe object public Recipe Recipe { get; set; } [Required(ErrorMessage = "At least one Tag is required.")] [Display(Name = "Select multiple tags:")] public int[] SelectedTagIds { get; set; }
public IEnumerable<SelectListItem> TagList { get; set; } }The Controller:
// GET: Recipe/Create public ActionResult Create() { ViewBag.Message = "Create Recipe"; var createRecipe = new CreateRecipe { Recipe = new Recipe(), SelectedTagIds = new[] { 2 }, TagList = GetAllTagTypes() }; return View(createRecipe); }
// POST: Recipe/Create [HttpPost] public ActionResult Create(CreateRecipe model) { try { // TODO: Add insert logic here return RedirectToAction("ViewRecipes"); } catch { return View(); } }
public List<SelectListItem> GetAllTagTypes() { List<SelectListItem> items = new List<SelectListItem>(); var data = TagProcessor.LoadTags(); foreach (var row in data) { items.Add(new SelectListItem { Text = row.Name, Value = row.TagId.ToString() }); }
return items; }The View:
<div class="col-md-3"> @Html.ListBoxFor(model => model.SelectedTagIds, Model.TagList, new { style = "width:200px;height:300px; padding:5px;", @onchange = "getSelectedTags(this)" }) @Html.ValidationMessageFor(model => model.SelectedTagIds, "", new { @class = "text-danger" }) </div>
Resources:
https://www.completecsharptutorial.com/asp-net-mvc5/html-listboxfor-and-html-listboxforfor-example-in-asp-net-mvc.php- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, September 25, 2020 11:00 PM