Reputation: 247
I'm doing a class lab which consists of designing a little Crowdfunding website. When the user creates a project, he is asked for how many tiers of rewards he wants for his project. If this amount is superior to 0, he's redirected to the Reward/Create page. The "Create" view has a form in it which is generated according to the amount of tiers the user wants.
The problem is that I have no idea how can I link each instance of the "for" loop to a particular "RewardViewModel" object with one single submit button, and how to pass all these objects to the "post" method in the controller.
Here is the controller "Get" method (I know I'll probably have to change the model binded to the view):
public ActionResult Create(int projectid, int amountOfTiers )
{
ViewBag.AmountOfTiers = amountOfTiers;
ViewBag.ProjectTitle = (from p in context.Projects where p.ProjectId == projectid select p).FirstOrDefault().Title;
RewardViewModel model = new RewardViewModel { ProjectId = projectid };
return View(model);
}
And here is the view :
@model CrowdFundMe.ViewModels.RewardViewModel
@{
ViewBag.Title = "Create";
}
<body>
@section Login{
@Html.Action("_Login", "User")
}
<h2>Create</h2>
<p>You are adding rewards for the project @ViewBag.ProjectTitle</p>
@using (Html.BeginForm("Create", "Reward", FormMethod.Post))
{
var u = ViewBag.AmountOfTiers;
for (int i = 0; i < u; i++)
{
var tier = i+1;
@Html.HiddenFor(model => model.ProjectId)
@Html.LabelFor(model => model.Tier, "Tier")<br />
@Html.TextBoxFor(model => model.Tier, new {@Value = tier, @readonly="readonly"})
<br />
<br />
@Html.LabelFor(model => model.Title, "Title")<br />
@Html.TextBoxFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
<br />
<br />
@Html.LabelFor(model => model.AmountOfItems, "Quantity available (leave blank if unlimited)")<br />
@Html.TextBoxFor(model => model.AmountOfItems)
@Html.ValidationMessageFor(model => model.AmountOfItems)
<br />
<br />
@Html.LabelFor(model => model.Description, "Description")<br />
@Html.EditorFor(model => model.Description)
@Html.ValidationMessageFor(model => model.Description)
<br />
<br />
}
<input type="submit" value="Create" />
}
Upvotes: 2
Views: 754
Reputation:
You wanting to create a collection of RewardViewModel
therefore your model in the view must be a collection. Currently you only passing one instance of RewardViewModel
and then using a for
loop to generate multiple form controls with duplicate name
attributes (and duplicate id
attributes which is invalid html).
Change the GET method to return a collection
public ActionResult Create(int projectid, int amountOfTiers)
{
List<RewardViewModel> model = new List<RewardViewModel>();
for(int i = 0; i < amountOfTiers; i++)
{
model.Add(new RewardViewModel { ProjectId = projectid });
}
ViewBag.ProjectTitle = (from p in context.Projects where p.ProjectId == projectid select p).FirstOrDefault().Title;
return View(model);
}
and the view to
@model List<RewardViewModel>
....
@using (Html.BeginForm("Create", "Reward", FormMethod.Post))
{
for(int i = 0; i < Model.Count; i++)
{
@Html.HiddenFor(m => m[i].ProjectId)
@Html.LabelFor(m => m[i].Title)
@Html.TextBoxFor(m => m[i].Title)
@Html.ValidationMessageFor(m => m[i].Title)
....
}
<input type="submit" value="Create" />
}
Which will correctly name your controls with indexers and will post to
public ActionResult Create(List<RewardViewModel> model)
Side note: remove new { @Value = tier }
(never set the value
attribute when using the HtmlHelper
methods) and its unclear why you have made the Tier
property readonly
- its value is not set in the controller and cannot be changed in the view so including it in the view seems pointless. If your want to add a consecutive number to the property, then do it in the controller - e.g. model.Add(new RewardViewModel { ProjectId = projectid, Tier = i + 1 });
Note also there is no need to use @Html.LabelFor(model => model.Tier, "Tier")
- it can just be @Html.LabelFor(model => model.Tier)
if the display text matches the property name.
Upvotes: 1