as in the title I am having issues with returning a model that has a list of models as one of the values. The goal is a page model that contains a few key values along with a user list and report list. but upon return either just the list values are null or the entire model has been remade with default values.
The ReportViewModel:
namespace DataLibrary.Models
{
public class ReportViewModel
{
[Key]
public string Id { get; set; } = "0";
public string Admin { get; set; } = "n";
public List<ReportModel> Reports { get; set; }
public List<UserModel> Users { get; set; }
public DateTime Start { get; set; } = DateTime.Now.AddDays(-(DateTime.Now.DayOfWeek - DayOfWeek.Monday));
public DateTime End { get; set; } = DateTime.Now;
}
}
With both UserModel and ReportModel having numerous values of string, BitArray, and int. I have created a more simple test view for my troubleshooting below. This is mainly to test just returning the lists as a whole without any editing as it should be easier, which still proves ineffective:
@model DataLibrary.Models.ReportViewModel
@{
ViewData["Title"] = "Report1";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<form asp-action="SelectDate">
<div class="container">
<div class="row form-group">
<input type="hidden" asp-for="@Model.Reports" />
<input type="hidden" asp-for="@Model.Users" />
<div class="col-4">
<input asp-for="@Model.Start"></input>
</div>
<div class="col-4">
<input asp-for="@Model.End"></input>
</div>
<div class="col-4">
<input type="submit" value="Select Date Range" class="btn btn-primary" />
</div>
</div>
@if (Model.Admin != "n")
{
<div class="row">
<input asp-for="Admin" hidden="hidden" />
@{
var perm1 = new Dictionary<string, string>
{
{"id", Model.Id},
{"adm", Model.Admin}
};
}
<div class="col-4"><a asp-controller="Timesheet" asp-action="AuthAll" asp-all-route-data="perm1" class="btn btn-primary">Authorize All</a></div>
<div class="col-4">
<select asp-for="Id">
@for (int e = 0; e < Model.Users.Count; e++)
{
if (Model.Users[e].USER_ID == Model.Id)
{
<option value="@Model.Users[e].USER_ID" selected="selected">@Model.Users[e].USER_FIRST_NAME @Model.Users[e].USER_LAST_NAME</option>
}
else
{
<option value="@Model.Users[e].USER_ID">@Model.Users[e].USER_FIRST_NAME @Model.Users[e].USER_LAST_NAME</option>
}
}
</select>
<input type="submit" value="Select User" class="btn btn-primary" />
</div>
</div>
}
</div>
</form>
In the actual view I originally used foreach but saw others saying that was not a good way to go about it and a basic for loop worked better? So I am currently using the bellow as the full view/editing the models inside the report list, but am using the above to troubleshoot the issue of not returning the lists and proper values. Upon submitting the below, the Reports list in the model sent to the controller has the correct number of entries but all values are NULL. This was the case with both the foreach and for loop versions:
@model DataLibrary.Models.ReportViewModel
@{
}
<form asp-action="SelectDate">
<div class="container">
<div class="row form-group">
<div class="col-4">
<input asp-for="@Model.Start"></input>
</div>
<div class="col-4">
<input asp-for="@Model.End"></input>
</div>
<div class="col-4">
<input type="submit" value="Select Date Range" class="btn btn-primary" />
</div>
</div>
@if(Model.Admin != "n")
{
<div class = "row">
<input asp-for="Admin" hidden="hidden"/>
@{var perm1 = new Dictionary<string, string>
{
{"id", Model.Id},
{"adm", Model.Admin}
};}
<div class="col-4"><a asp-controller="Timesheet" asp-action="AuthAll" asp-all-route-data="perm1" class="btn btn-primary">Authorize All</a></div>
<div class="col-4">
<select asp-for="Id">
@for(int e = 0; e< Model.Users.Count; e++)
{
if (Model.Users[e].USER_ID == Model.Id)
{
<option value="@Model.Users[e].USER_ID" selected="selected">@Model.Users[e].USER_FIRST_NAME @Model.Users[e].USER_LAST_NAME</option>
}
else
{
<option value="@Model.Users[e].USER_ID">@Model.Users[e].USER_FIRST_NAME @Model.Users[e].USER_LAST_NAME</option>
}
}
@* @foreach (var user in Model.Users)
{
if (user.USER_ID == Model.Id)
{
<option value="@user.USER_ID" selected="selected">@user.USER_FIRST_NAME @user.USER_LAST_NAME</option>
}
else
{
<option value="@user.USER_ID">@user.USER_FIRST_NAME @user.USER_LAST_NAME</option>
}
} *@
</select>
<!-- send model and select based on values, date, user, etc-->
<input type="submit" value="Select User" class="btn btn-primary" />
</div>
</div>
}
</div>
</form>
<form asp-action="Update">
<div class="form-group">
<table class="ReportTable " >
<thead>
<tr class="ReportRows">
<th>
@Html.DisplayName("Date")
</th>
<th>
@Html.DisplayName("Attendance")
</th>
<th>
@Html.DisplayName("Hours")
</th>
<th>
@Html.DisplayName("Over")
</th>
<th>
@Html.DisplayName("Update/Unauth")
</th>
<th>
@Html.DisplayName("Authenticate")
</th>
<th>
@Html.DisplayName("Delete")
</th>
</tr>
</thead>
<tbody>
<input asp-for="@Model.Start" type="hidden"></input>
<input asp-for="@Model.End" type="hidden"></input>
<input type="hidden" value="@Model.Reports" />
<input type="hidden" value="@Model.Users" />
@for (int i = 0; i< Model.Reports.Count; i++)
{
<div class="form-group">
<tr class="ReportRows">
@* <input type="hidden" value="@Model.Reports[i].SALARIED1" />
<input type="hidden" value="@Model.Reports[i].PRESENT1" />
<input type="hidden" value="@Model.Reports[i].PRESENT_AFTERNOON1" /> *@
<th>
@Html.DisplayName(Model.Reports[i].DATE1.ToString())
</th>
<th>
@if (Model.Reports[i].SALARIED1[0] == true)
{
//if salaried show am pm
<input type="checkbox" asp-for="@Model.Reports[i].PRESENT1[0]" />
<input type="checkbox" asp-for="@Model.Reports[i].PRESENT_AFTERNOON1[0]" />
}
else
{
//TimeOnly t = TimeOnly.FromDateTime(entry.CLOCKIN1);
<input type="text" asp-for="@Model.Reports[i].CLOCKIN1" />
<input type="text" asp-for="@Model.Reports[i].CLOCKOUT1" />
// if not salaried show times
}
</th>
<th>
@if (Model.Reports[i].SALARIED1[0] == true)
{
@Html.DisplayName("N/A")
}
else
{
@Html.DisplayName(Model.Reports[i].totalminute_work1.ToString())
}
</th>
<th>
@if (Model.Reports[i].SALARIED1[0] == true)
{
@Html.DisplayName("N/A")
}
else
{
@Html.DisplayName(Model.Reports[i].over_time1.ToString())
}
</th>
<th>
@if (Model.Reports[i].AUTHORIZED1 != null && Model.Reports[i].AUTHORIZED1[0] == true)
{
// display button to unauth
var perm = new Dictionary<string, string>
{
{"Date", Model.Reports[i].DATE1.ToString()},
{"id", Model.Reports[i].USER_ID1},
{"tsid", Model.Reports[i].ID1.ToString()},
{"adm", Model.Admin }
};
<a asp-controller="Timesheet" asp-action="UnAuth" asp-all-route-data="perm" class="btn">UnAuth</a>
@* <a asp-controller="Timesheet" asp-action="Unauth" class="btn">UnAuth</a> *@
}
else
{
var perm4 = new Dictionary<string, string>
{
{"Date", Model.Reports[i].DATE1.ToString()},
{"Uname", Model.Reports[i].USER_ID1},
{"tsid", Model.Reports[i].ID1.ToString()},
{"adm", Model.Admin } ,
{"p1", Model.Reports[i].PRESENT1[0].ToString()}
//,
//{"p2", Model.Reports[i].PRESENT_AFTERNOON1[0].ToString()}
};
// update times button
<a asp-controller="Timesheet" asp-action="Update" asp-all-route-data="perm4" class="btn">Update</a>
<button type="submit" class="btn btn-primary">Update</button>
}
</th>
<th>
@if (Model.Reports[i].AUTHORIZED1 != null && Model.Reports[i].AUTHORIZED1[0] == true)
{
}
else
{
var perm = new Dictionary<string, string>
{
{"Date", Model.Reports[i].DATE1.ToString()},
{"Uname", Model.Reports[i].USER_ID1},
{"t", null},
{"adm", Model.Admin }
};
<a asp-controller="Timesheet" asp-action="Auth" asp-all-route-data="perm" class="btn">Auth</a>
@* asp - Date = "@entry.DATE1" asp - Uname = "@entry.USER_ID1" asp - t = "" asp - model = "@Model" *@
@* @Html.ActionLink("Auth", "Auth", new{Date = entry.DATE1}, new{Uname = entry.USER_ID1}, new {t = ""}) *@
}
</th>
<th>
@if (Model.Reports[i].AUTHORIZED1 != null && Model.Reports[i].AUTHORIZED1[0] == true)
{
}
else
{
var perm3 = new Dictionary<string, string>
{
{"Date", Model.Reports[i].DATE1.ToString()},
{"Uname", Model.Reports[i].USER_ID1},
{"tsid", Model.Reports[i].ID1.ToString()},
{"adm", Model.Admin }
};
<a asp-controller="Timesheet" asp-action="DeleteClock" asp-all-route-data="perm3" class="btn">Delete</a>
}
</th>
</tr>
</div>
}
</tbody>
</table>
</div>
</form>
The controller parts are pretty simple:
public IActionResult Report1()
{
if (HttpContext.Session.GetString("User") != null)
{
// get info here and pass into view based on session name
// need to create option for admins to select different users, and allow all users to select different
// date range
ReportViewModel model = new ReportViewModel();
List<ReportModel> reports = //redacted database code;
// session to stand in for validation until I learn that more thoroughly.
if (HttpContext.Session.GetString("Admin") != "n")
{
entries = _db.user.ToList();
users = entries.ToList();
}
model.Id = HttpContext.Session.GetString("User");
model.Reports = reports;
model.Users = users;
model.Admin = HttpContext.Session.GetString("Admin");
return View(model);
}
else
{
return RedirectToAction("Login");
}
}
[HttpPost]
public IActionResult Report1(ReportViewModel model)
{
string test = "";
return View(model);
}
I apologize as I am still learning a lot about this system, and figured this layout would make for good practice but have hit this roadblock.
as stated above I have attempted to create a simple version of the view to just return the model that is sent in without many changes except for the non list parameters, which still leads to the lists being null. I have attempted setting the name="" attributes on all values to what is listed on the original models, to no avail. I am not sure what is causing the issue of not returning the filled lists at this time. Unfortunately other questions seemed to brush this topic but the one that seemed the closest I could find here did not seem to offer any further insight.
Looking at the similar posts it appears I may be missing form-control? I am somewhat burnt out from looking at this so long, so I will return to this later.
I am having issues with returning a model that has a list of models as one of the values. The goal is a page model that contains a few key values along with a user list and report list. but upon return either just the list values are null or the entire model has been remade with default values
Well, based on your scenario and description along with the shared code snippet I have tried to investigate your issue and successfylly reproduced it.
I hope you are getting following null model bindings while submitting the form:
Investigation Result:
First of all, your submit form and the controller action doesn't have consistencies because its not calling the method you been pointed. So I have fixed that one.
Apart from that, within your code you have tried to bind the model value following this way:
<input type="hidden" value="@Model.Reports[i].PRESENT1" />
Which actually incorrect and cannot bind the model value accurately while you are submitting the form. Instead you should bind your model within the for loop like below:
For Report model, you should bind he model value this way:
<input type="hidden" asp-for="@Model.Reports[i].Id" />
Same goes for User Model as well:
<input type="hidden" asp-for="@Model.Users[i].USER_ID" />
In addition, I noticed, you are binding a select list. In my test I have slidely refactor your code in order to look clean and simpler.
Here is the complete sample I tried the test with:
<form asp-action="Update" asp-controller="ModelListTest" method="post">
@for (int i = 0; i < Model.Reports.Count; i++)
{
<input type="hidden" asp-for="@Model.Reports[i].Id" />
<input type="hidden" asp-for="@Model.Reports[i].Name" />
}
@for (int i = 0; i < Model.Users.Count; i++)
{
<input type="hidden" asp-for="@Model.Users[i].USER_ID" />
<input type="hidden" asp-for="@Model.Users[i].USER_FIRST_NAME" />
<input type="hidden" asp-for="@Model.Users[i].USER_LAST_NAME" />
}
<div class="col-4">
<select asp-for="Id" asp-items="@(new SelectList(Model.Users, "USER_ID", "USER_FIRST_NAME"))">
<option value="">Select User</option>
</select>
<input type="submit" value="Select User" class="btn btn-primary" />
</div>
</form>
Note: Remember, this not the exact solution to your program, I just tried to assist you why you are getting null list value within your controller.
Output:
Note: Please refer to this official document in order to achieve detail insight of model binding.