Reputation: 1965
I have a model as such:
public class ImageFileDetail {
public HttpPostedFileBase File { get; set; }
public bool IsMainImage { get; set; }
public string SourceURL { get; set; }
}
I populate the razor view with a List<ImageFileDetail>
with a count of 10, allowing the user to upload 10 images.
Only one image can be the IsMainImage
.
I have to do it in a an @for
loop instead of a @foreach
, otherwise the model binder won't bind to each individual object in the list. Doing a @foreach
gives them all the same name=
when the form is posted, so I can't iterate it as separate objects.
View:
@for (var i = 0; i < Model.ImageFileDetails.Count(); i++)
{
<tr>
<td>
@Html.TextBoxFor(x => x.ImageFileDetails[i].File, new { type = "file" })
</td>
<td>
@Html.EditorFor(x => x.ImageFileDetails[i].SourceURL, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(x => x.ImageFileDetails[i].SourceURL, "", new { @class = "text-danger" })
</td>
<td>
@*Html.EditorFor(x => x.ImageFileDetails[i].IsMainImage)*@
// this is the radio button
@Html.RadioButton("UniqueIsMainImage", Model.ImageFileDetails[i].IsMainImage)
@Html.ValidationMessageFor(x => x.ImageFileDetails[i].IsMainImage, "", new { @class = "text-danger" })
</td>
</tr>
}
By giving the radio button the same name in the @RadioButton
, that allows me to only let one be selected, so that's good. When one is selected, the prior one deselects, which is the desired result.
The problem is that on POST
, I don't get a value of true
for the selected radio button in the list and I'm not sure why.
What am I doing wrong?
Upvotes: 0
Views: 2898
Reputation: 10248
This is probably not entirely what you're looking for but in relation to your for/foreach statement - razor will actually do this for you if you let it:
Also you should be using RadioButtonFor I think.
You can leverage the templating in razor view and let it handle the list/array for you. So rather than:
@Html.EditorFor(x => x.ImageFileDetails[i].SourceURL,...
You should be doing this (Note the name of template file should match the TYPE name):
@Html.EditorFor(x => x.ImageFileDetails)
Then create a EditorTemplates/ImageFileDetail.cshtml template:
@model <namespace>.ImageFileDetail
<tr>
<td>
@Html.TextBoxFor(x => x.File, new { type = "file" })
</td>
<td>
@Html.EditorFor(x => x.SourceURL, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(x => x.SourceURL, "", new { @class = "text-danger" })
</td>
<td>
@*Html.EditorFor(x => x.IsMainImage)*@
// this is the radio button
@Html.RadioButtonFor(x => x.IsMainImage, x.IsMainImage, new { name = "UniqueIsMainImage" })
@Html.ValidationMessageFor(x => x.IsMainImage, "", new { @class = "text-danger" })
</td>
</tr>
Upvotes: 0
Reputation: 1965
I had to change the radio button to @Html.RadioButtonFor(x => Model.SelectedImageId, Model.ImageFileDetails[i].Id)
and pass an Id
for each object in my list.
ViewModel:
public class ViewModel
{
public List<ImageFileDetail> ImageFileDetails { get; set; } = new List<ImageFileDetail>();
public int? SelectedImageId { get; set; }
}
public class ImageFileDetail {
public int Id { get; set; } // for main image radio button
public HttpPostedFileBase File { get; set; }
public bool IsMainImage { get; set; }
public string SourceURL { get; set; }
}
View:
@for (var i = 0; i < Model.ImageFileDetails.Count(); i++)
{
<tr>
<td>
@Html.TextBoxFor(x => x.ImageFileDetails[i].File, new { type = "file" })
</td>
<td>
@Html.EditorFor(x => x.ImageFileDetails[i].SourceURL, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(x => x.ImageFileDetails[i].SourceURL, "", new { @class = "text-danger" })
</td>
<td>
@*Html.EditorFor(x => x.ImageFileDetails[i].IsMainImage)*@
@Html.RadioButtonFor(x => Model.SelectedImageId, Model.ImageFileDetails[i].Id)
@Html.ValidationMessageFor(x => x.ImageFileDetails[i].IsMainImage, "", new { @class = "text-danger" })
</td>
</tr>
}
Then in my controller, when iterating through the posted list, I set the IsMainImage
to true for the SelectedImageId
.
Post Controller:
foreach (var fileDetail in viewModel.ImageFileDetails)
{
if (fileDetail.Id == viewModel.SelectedImageId)
fileDetail.IsMainImage = true;
}
Thanks everyone for believing in me.
Upvotes: 1