What possible reason could there be for this razor code:
// ShowSelectedMembers.cshtml
@model MyApp.Models.MembersViewModel
@for (int m = 0; m < Model.Members.Count; m++)
{
<input type="hidden" asp-for="@Model.Members[m].Id" />
<input type="hidden" asp-for="@Model.Members[m].FirstName" />
<input type="hidden" asp-for="@Model.Members[m].LastName" />
<span>[debug-info: m = @m, id = @Model.Members[m].Id]</span>
<span>@Model.Members[m].LastName</span>,
<span>@Model.Members[m].FirstName</span>
}
to produce this HTML, when Model.Members.Count = 1, just containing the member with id 6653:
<input type="hidden" data-val="true" data-val-required="The Id field is required." id="Members_0__Id" name="Members[0].Id" value="6652" />
<input type="hidden" id="Members_0__FirstName" name="Members[0].FirstName" value="Peter" />
<input type="hidden" id="Members_0__LastName" name="Members[0].LastName" value="Hanson" />
<span>[debug-info: m = 0, id = 6653]</span>
<span>Swanson</span>,
<span>Lisa</span>
How can Members[0].Id have the value of 6652 in the hidden field, and 6653 inside the <span>?
This is the controller method for the view:
public IActionResult ShowSelectedMembers(MembersViewModel vm)
{
vm.Members = vm.Members.Where(s => s.Selected).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return View(vm);
}
This is the form which sends the whole member list to the controller method:
// Index.cshtml
@model MyApp.Models.MembersViewModel
<form asp-action="ShowSelectedMembers">
<button type="submit">View selection</button>
@if (Model.Members.Any())
{
for (int i = 0; i < Model.Members.Count; i++)
{
Model.Members[i].Id
Model.Members[i].FirstName
Model.Members[i].LastName
<input type="checkbox" asp-for="@Model.Members[i].Selected" />
<input type="hidden" asp-for="@Model.Members[i].Id" />
<input type="hidden" asp-for="@Model.Members[i].FirstName" />
<input type="hidden" asp-for="@Model.Members[i].LastName" />
}
}
</form>
When the form data is sent to the controller method, it contains all the members in that view. The method then filters out all members with Selected set to false.
These are the ViewModels:
public class MembersViewModel
{
// ... some more properties
public List<MemberViewModel> Members { get; set; }
}
public class MemberViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Selected { get; set; }
// ... some more properties
}
UPDATE
To clarify the flow of this operation:
The Index-view contains a form with
Selected-checkboxes for each member in the list.This form passes the whole list of members to the controller method
ShowSelectedMembers.The controller method filters out members with
Selectedset tofalse, and passes the selected members to the viewShowSelectedMembers.The view displays the filtered list, which now has corrupted data.
UPDATE 2
I changed the controller method to this, creating a new instance instead of reusing the viewmodel:
public async Task<IActionResult> ShowSelectedMembers(MembersViewModel vmIn)
{
List<int> memberIds = new List<int>();
foreach (MemberViewModel member in vmIn.Members.Where(s => s.Selected).ToList())
{
memberIds.Add(member.Id);
}
List<Member> selectedMembers = await db.Members
.Where(s => memberIds.Contains(s.Id))
.ToListAsync();
MembersViewModel vmOut = new MembersViewModel {
Members = auto.Map<List<MemberViewModel>>(selectedMembers)
};
return View(vmOut);
}
... but the result is exactly the same.
UPDATE 3
This is so weird!
It appears to be working correctly when I change the razor code to this in ShowSelectedMembers.cshtml:
@*<input type="text" asp-for="@Model.Members[i].Id" />*@
@{
string _id = $"Members_{i}__Id";
string _name = $"Members[{i}].Id";
string _value = Model.Members[i].Id.ToString();
}
<input type="hidden"
data-val="true"
data-val-required="The Id field is required."
id=@_id
name=@_name
value=@_value />
I.e. manually generating the input field rather than using asp-for.