How to represent a month of checkboxes in an MVC model - c#

I can't manage to get my head around how to use MVC to create the following table, and successfully bind it to a model:
I basically need to keep track of which days of the month an event needs to happen. Here is my attempt at a model:
EDIT: This isn't for a month, but for a arbitrary 4 week cycle
public class ScheduleViewModel
{
public int PatientId { get; set; }
public List<Schedule> Schedules { get; set;}
}
public class Schedule {
public int Week { get;set;}
public Day Day { get;set;}
public bool IsSelected { get;set;}
}
public enum Day
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
And I can render a view successfully (that isn't bound to the model). I realise that I would need to use #html.CheckBoxFor in place on my inputs.
Here is a rough copy of my html for the view:
#model WebApplication10.Models.ScheduleViewModel
#using (Html.BeginForm())
{
<table class="table table-striped">
<thead>
<tr>
<th></th>
#{
foreach (Day t in Enum.GetValues(typeof(Day)))
{
<th>#t</th>
}
}
</tr>
</thead>
<tbody>
#{
for (int i = 1; i <= 4; i++)
{
<tr>
<td>Week #i</td>
#foreach (Day t in Enum.GetValues(typeof(Day)))
{
<td><input type="checkbox" /></td>
}
</tr>
}
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
How would I successfully post which check-boxes have been selected? Does my ViewModel make sense?

I suggest you change you view model(s) to
public class DayVM
{
public Day Day { get; set; }
public bool IsSelected { get; set; }
}
public class WeekVM
{
public WeekVM()
{
Days = new List<DayVM>();
Days.Add(new DayVM() { Day = Day.Sunday });
Days.Add(new DayVM() { Day = Day.Monday });
.. etc
}
public List<DayVM> Days { get; set; }
}
public class ScheduleViewModel
{
public ScheduleViewModel()
{
Weeks = new List<WeekVM>();
Weeks.Add(new WeekVM());
.... etc
}
public int PatientId { get; set; }
public List<WeekVM> Weeks { get; set;}
}
Then in the view
for(int i = 0; i < Model.Weeks.Count; i++)
{
<tr>
<td>Week #i</td>
for(int j = 0; j < Model.Weeks[i].Days.Count; j++)
{
<td>
#Html.CheckBoxFor(m => m.Weeks[i].Days[j].IsSelected)
</td>
}
</tr>
}
Side note: I don't think you really need you own enum here - your could just use the DayOfWeek enumeration, for example Days.Add(new DayVM() { Day = DayOfWeek.Sunday });. Note also I have not included an input for the Day property since you could easily determine that from its index in the collection. In fact the Day property may not be required at all if you manually render the table header row.

Related

Is it possible to check/uncheck a checkbox based on other checkboxes state?

I am trying to create a simple lunch scheme webapp, where you can sign up for any business day of the week. I want the user to be able to select each day individually or select a whole week.
What I thought would be a simple method call/check is proving to be seemingly impossible in a Blazor razorpage. I want a checkbox for the whole week, which checks or unchecks all the checkboxes for each individual day. This is pretty straightforward and works as expected. But if I uncheck any of the days, I want the week checkbox to get unchecked as well.
This is what the relevant part of my page looks like:
#foreach (var item in weeks)
{
<tr>
<td><input type="checkbox" #onchange="eventArgs => SetAllWeekDays(item.Key, eventArgs.Value)" /> #item.Value.WeekNumber</td>
<td>
<input type="checkbox" #bind="item.Value.WeekDays[0].IsSignedUp" />
</td>
<td>
<input type="checkbox" #bind="item.Value.WeekDays[1].IsSignedUp" />
</td>
<td>
<input type="checkbox" #bind="item.Value.WeekDays[2].IsSignedUp" />
</td>
<td>
<input type="checkbox" #bind="item.Value.WeekDays[3].IsSignedUp" />
</td>
<td>
<input type="checkbox" #bind="item.Value.WeekDays[4].IsSignedUp" />
</td>
</tr>
}
And this is the SetAllWeekDays method:
private void SetAllWeekDays(int weekNumber, object checkedValue)
{
if ((bool)checkedValue)
{
for (int i = 0; i < weeks[weekNumber].WeekDays.Count; i++)
{
weeks[weekNumber].WeekDays[i].IsSignedUp = true;
}
}
else
{
for (int i = 0; i < weeks[weekNumber].WeekDays.Count; i++)
{
weeks[weekNumber].WeekDays[i].IsSignedUp = false;
}
}
}
When I check or uncheck the week, it checks or unchecks all the days, but if I check or uncheck any of the days individually, the checkbox for the week stays unchanged
I feel like I'm missing some obvious way of databinding a method call to the value of the whole week, as you can't both bind a value and set an #onchange event.
Here's a working code sample how to do that... copy and test:
Note that #bind is equivalent to the checked attribute and the #onchange directive, and how I make use of both
#page "/LunchScheme"
#using System;
<table class="table">
<thead>
<tr>
<td>Week</td>
<td>Monday</td>
<td>Tuesday</td>
<td>Wednesday</td>
<td>Thursday</td>
<td>Friday</td>
</tr>
</thead>
<tbody>
#foreach(var week in weeks)
{
<tr>
<td>
<input type="checkbox" checked="#week.Selected" #onchange="#((args) => { week.Selected = (bool)
args.Value; Check(week); } )" />
</td>
#foreach(var day in week.DayOfTheWeeks)
{
<td>
<input type="checkbox" checked="#day.Selected" #onchange="#((args) => { day.Selected = (bool)
args.Value; CheckWeek(week); } )" />
</td>
}
</tr>
}
</tbody>
</table>
#code {
private List<Week> weeks = new List<Week>() { new Week { ID = 1 },
new Week { ID = 2 },
new Week { ID = 3 },
new Week { ID = 4 }};
public class Week
{
public int ID { get; set; }
public bool Selected { get; set; }
public List<DayOfTheWeek> DayOfTheWeeks { get; set; } = new List<DayOfTheWeek>()
{ new DayOfTheWeek{ DayOfWeek = DayOfWeek.Monday },
new DayOfTheWeek{ DayOfWeek = DayOfWeek.Tuesday },
new DayOfTheWeek{ DayOfWeek = DayOfWeek.Wednesday },
new DayOfTheWeek{ DayOfWeek = DayOfWeek.Thursday },
new DayOfTheWeek{ DayOfWeek = DayOfWeek.Friday }};
}
public class DayOfTheWeek
{
public DayOfWeek DayOfWeek {get; set;}
public bool Selected {get;set;}
}
protected void Check(Week week)
{
var toCheck = week.DayOfTheWeeks;
foreach (var day in toCheck)
{
day.Selected = week.Selected;
}
}
protected void CheckWeek(Week week)
{
var allSelected = week.DayOfTheWeeks.All(day => day.Selected == true);
if(allSelected)
{
week.Selected = true;
}
else
{
week.Selected = false;
}
}
}

Not able to bind value to model which is collection asp.net core mvc

I have list as model in my view. I display my items information and counts. also I have to take assignedworker to that location from the user. When I submit this method, my collectionmodel is getting null. I am losing all information in my model.
I have data in all the properties except assignedworker. I display all the information to the user using foreach and I take assignedworker name from the user. When I submit this form, List is null.
public class Report
{
public string itemname{ get; set; }
public List<itemlocation> locations { get; set; }
}
public class itemlocation
{
public string location { get; set; }
public List<items> items{ get; set; }
public string assignedworker{ get; set; }
}
View:
#model IList<Report>
<form method="post" asp-action="Report" asp-controller="Home">
#foreach (var rep in Model)
{
<tr>
<td colspan="3">
<h3>#rep.itemname</h3>
</td>
</tr>
#foreach (var loc in rep.itemlocation)
{
<tr>
<td>#loc.location </td>
<td>#loc.items.Count()</td>
<td>
<input type="text" class="form-control" id="worker" name="#loc.assignedworker" value="#loc.assignedworker">
</td>
</tr>
}
}
</form>
I have data in all the properties except assignedworker. I display all the information to the user using foreach and I take assignedworker name from the user. When O submit this form, List is null.
Expected result:
In my controller I would like to be able to see my collection(List) with all the values including assignedworker.
In order to generate the right input names for modelbinding, Razor needs the full model expression, which means you must use a regular for loop and index your lists, rather than using foreach:
#for (var i = 0; i < Model.Count; i++)
{
...
#for (var j = 0; j < Model[i].locations.Count; j++)
{
...
<input asp-for="#Model[i].locations[j].assignedworker" />
...
}
}
You could use <input asp-for="" hidden /> to pass the value to the action:
1.Model:
public class Report
{
public string itemname { get; set; }
public List<itemlocation> locations { get; set; }
}
public class itemlocation
{
public string location { get; set; }
public List<items> items { get; set; }
public string assignedworker { get; set; }
}
public class items
{
public int Id { get; set; }
public string name { get; set; }
}
2.View:
#model IList<Report>
<form method="post" asp-action="Report" asp-controller="Home">
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td colspan="3">
<input asp-for="#Model[i].itemname" hidden/>
<h3>#Model[i].itemname</h3>
</td>
</tr>
#for (var j = 0; j < Model[i].locations.Count; j++)
{
<tr>
<td>
#Model[i].locations[j].location
<input asp-for="#Model[i].locations[j].location" hidden />
</td>
<td>
#Model[i].locations[j].items.Count()
#for (var k = 0; k < Model[i].locations[j].items.Count; k++)
{
<input asp-for="#Model[i].locations[j].items[k].Id" hidden />
<input asp-for="#Model[i].locations[j].items[k].name" hidden />
}
</td>
<td>
<input asp-for="#Model[i].locations[j].assignedworker" />
</td>
</tr>
}
}
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>

Radio button values binding not working

I have a view:
#using(Html.BeginForm())
{
<div>
<table id="dimensionFeedbackTable">
<tbody>
#for(int i = 0; i < 6; i++)
{
<tr>
<td>
<div>
<p>
<text>
Rate #i
</text>
</p>
</div>
</td>
#foreach(var item in Model.DeptList)
{
<td>
<div style="text-align: center;">
#Html.RadioButtonFor(m => item.DepartmentValue, i,
new
{
id = i,
style = "min-height:45px;"
})
</div>
</td>
}
</tr>
}
</tbody>
</table>
<button id="submitComment" value="submit">
#{
<text>Submit</text>
}
</button>
</div>
}
Model in that view is EmployeeModelClass. Here it is:
public class EmployeeModelClass
{
public int Id
{
get; set;
}
public IEnumerable<DepartmentModelClass> DeptList
{
get; set;
}
}
public class DepartmentModelClass
{
public string DepartmentCode
{
get; set;
}
[Range(1, 6)]
public int? DepartmentValue
{
get; set;
}
}
My controller:
[HttpPost]
public ActionResult Index(EmployeeModelClass emp, IEnumerable<DepartmentModelClass> qec)
{
emp.DeptList = qec;
return View(emp);
}
I am trying to accomplish next:
Each row should be group for it self. For example Rate 1 row can have only one radio button selected. Now, these buttons should be bound. If second radio button is selected in any row then its value (DepartmentValue) should be set to 2. So when I submit, I get a collection of which radio buttons are selected.
I've tried a lot of combinations with setting proper model and binding it. Also tried html helper method RadioButton, but still no luck to return the values. Closest that I managed to get is returning a collection but although radio buttons are selected in Controller, collection is not null but values (DepartmentValues) are null.
Also, there are similar answers here but for some reason none of them is working for me. Any help and pointing direction are appreciated. Losing my mind over a simple thing.
One more thing, I tried with creating partials view but still now luck. I read online that is a bad practice to place for/foreach loop in a view and tried to simplify it but this approach is also not working.
Thanks
I notice few issues -
for loop and foreach loop are opposite.
Should use for for array of controls instead of foreach
Do not explicitly assign id to radio buttons new { id = i, ...})
View
#model DemoMvc.Models.EmployeeViewModel
#using (Html.BeginForm())
{
<div>
<table id="dimensionFeedbackTable">
<tbody>
#for (int i = 0; i < Model.DepartmentViewModels.Count; i++){
<tr>
<td>
<div>
<p>
<text>
Rate #Model.DepartmentViewModels[i].DepartmentCode
</text>
</p>
</div>
</td>
#for (int j = 1; j <= 6; j++)
{
<td>
<div style="text-align: center;">
#Html.RadioButtonFor(m =>
Model.DepartmentViewModels[i].DepartmentValue,
j, new { style = "min-height:45px;" })
#j
</div>
</td>
}
</tr>
}
</tbody>
</table>
<button id="submitComment" value="submit">Submit</button>
</div>
}
Model
public class EmployeeViewModel
{
public int Id { get; set; }
public IList<DepartmentViewModel> DepartmentViewModels { get; set; }
}
public class DepartmentViewModel
{
public string DepartmentCode { get; set; }
public int? DepartmentValue { get; set; }
}
Controller
public ActionResult Index()
{
// This could come from database.
var model = new EmployeeViewModel
{
DepartmentViewModels = new List<DepartmentViewModel>
{
new DepartmentViewModel{ DepartmentCode = "ABC", DepartmentValue = 1},
new DepartmentViewModel{ DepartmentCode = "DEF", DepartmentValue = 2},
new DepartmentViewModel{ DepartmentCode = "GHI", DepartmentValue = 3},
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(EmployeeViewModel model)
{
return View(model);
}
Result
Posted Value

pass table values from view to controller mvc

I am working on a project in dot Net + share-point,
In there controller get sharepoint list record & create a list of list object.
In view catch the object as one model and dilplay in a table.
Table view
enter image description here
Index.cshtml
#using( Html.BeginForm() )
{
<table id = "timeTrackingView" border="1" >
<tr>
<th>Projects</th>
#for (var date = #Model.Dates.AddDays(-(#Model.DateRange-1)); date <= #Model.Dates; date = date.AddDays(1))
{
<th >#date.Day/#date.Month/#date.Year</th>
}
</tr>
#{ double[] totalForToday = new double[#Model.DateRange];}
#for (int i = 0; i < #Model.TimeTrakings.Count; i++)
{
//var projectName = #Model.TimeTrakings[i][0].ProjectName;
int index = 0;
<tr>
<td>
#Model.TimeTrakings[i][0].ProjectName
</td>
#for (int j = 0; j < Model.TimeTrakings[i].Count(); j++)
{
totalForToday[index] = totalForToday[index] + #Model.TimeTrakings[i][j].Hours;
var time = #Html.EditorFor(model => #Model.TimeTrakings[i][j]);
<td>#time</td>
#*#Html.EditorFor(model => #Model.TimeTrakings[i][j].Hours)*#
index++;
}
</tr>
}
<tr>
<td>Total for day</td>
#foreach(var tot in totalForToday)
{
<td>#tot</td>
}
</tr>
</table>
<input type="submit" name="SubmitBtn"/>
}
I am trying to get table data into controller.. help me im my code only gettinh hours, need get project name date & hours together.
My controller.
[HttpPost]
public ActionResult Index(TimeTracking timeTracking)
{
////do the logic here
return View(this.ReceiveTimeTrackingData());
}
I Used two mode-:
Model1
public class TimeTrackingDetails
{
public string ProjectName { get; set; }
public DateTime Date { get; set; }
public double Hours { get; set; }
}
Model 2-:
public class TimeTracking
{
public List<List<TimeTrackingDetails>> TimeTrakings { get; set; }
}
If you need project name you should create an input. Now in your code you have just plain text with this line:
#Model.TimeTrakings[i][0].ProjectName
Just add another line onder it:
#Model.TimeTrakings[i][0].ProjectName
// this line will create additional hidden input and on form post it will be sended to server.
#Html.HiddenFor(x => x.TimeTrakings[i][0].ProjectName)

Should I use ViewBag to pass a list to a View?

Not sure why I got down voted but I'm going to re-write my question after doing some research and testing. This is a side project that I'm using to learn MVC/EF/Repository/Bootstrap etc. I only get a couple hours here a few nights a week to work on it.
Basic original question:
I know I should really be using a List<> in a ViewModel to pass the data to the View but I'm not sure how or if it will meet my requirement.
What I am trying to do is get a list of users to display in a table which would have a checkbox in each row. Above that table I want to have a list of Groups to that they could be assigned to. You select the section from the DropDownList (DDL) and then check who you want to assign it to. It's the groups/sections that I want want to assign as a list and pass to the View.
So, I've got a ViewModel with a list and I'm using a repository to populate the VM. What I don't know how to do exactly is where/when to populate that list with each VM object and even if I do and there are say 50 users I wouldn't want to make 50 trips to the DB to pull the same information.That is why I'm thinking that for this scenario using the ViewBag to pass that Group list to the View might be justifiable. On the flip side I would like to learn how to populate that list properly in the VM for future coding.
Updated question/code:
So, after more research and following some suggestions I've now got the following code. I'm still not sure how I will properly populate my Patrols in my ViewModel in order to populate the DDL in my View.
At the moment I've got the View displaying the table with the checkboxes. Now I'm back to working on getting the values to populate the DDL and then I'll have to work on posting to the controller, looping to find the check rows, and updating the database. In my case each member record is defaulted to a PatrolId=0 and this page should allow me to update the PatrolId to a value from the DDL.
The Patrols property in the PatrolMemberViewModel should be a list of about 5 records that I would pull from a DB table instead of hard coding in the DDL.
ViewModel:
public class PatrolViewModel
{
public int PatrolId { get; set; }
public string PatrolName { get; set; }
}
public class PatrolMemberViewModel
{
[Key]
public int MemberId { get; set; }
public int PatrolId { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Phone")]
public string PhonePrimary { get; set; }
[Display(Name = "Email")]
public string EmailPrimary { get; set; }
public bool IsSelected { get; set; }
public PatrolViewModel Patrols { get; set; }
}
Controller:
public ViewResult Unassigned()
{
try
{
IEnumerable<PatrolMemberViewModel> model = repository.SelectAllUnassigned();
return View(model);
}
catch (Exception)
{
ModelState.AddModelError(string.Empty, "Error retrieving the record.");
return View();
}
}
Repository:
public IEnumerable<PatrolMemberViewModel> SelectAllUnassigned()
{
using (DataContext db = new DataContext())
{
var results = (from p in db.Person
where p.IsActive == true
&& p.IsScout == true
&& p.PatrolId == 0
select new PatrolMemberViewModel
{
MemberId = p.PID,
FirstName = p.FirstName ?? string.Empty,
LastName = p.LastName ?? string.Empty,
EmailPrimary = p.EmailPrimary ?? string.Empty,
PhonePrimary = p.PhonePrimary ?? string.Empty,
PatrolId = p.PatrolId,
IsSelected = false
}
).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return results;
}
}
View:
#model IList<ProjectName.ViewModels.PatrolMemberViewModel>
#{
ViewBag.Title = "Unassigned";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Patrols</h2>
#using (Html.BeginForm("Update", "Patrol", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false, "", new { #class = "alert alert-danger" })
<table class="table table-bordered table-striped table-hover table-condensed tbackground">
<tr>
<th class="text-center">
</th>
<th class="text-center">
First Name
</th>
<th class="text-center">
Last Name
</th>
<th class="text-center">
Email
</th>
<th class="text-center">
Phone
</th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
<td class="text-center">
#Html.CheckBoxFor(m => m[i].IsSelected)
</td>
<td>
#Html.DisplayFor(m => m[i].FirstName)
</td>
<td>
#Html.DisplayFor(m => m[i].LastName)
</td>
<td>
#Model[i].EmailPrimary
</td>
<td class="text-center">
#Html.DisplayFor(m => m[i].PhonePrimary)
</td>
</tr>
}
</table>
<div class="control-wrapper">
<input type="submit" id="btnSubmit" value="Assign" class="btn btn-success" />
</div>
}
<p> </p>
Start by creating the view models to represent what you want to display/edit in the view. Your PatrolMemberViewModel can be used but remove the [Key] attribute and the int PatrolId and PatrolViewModel Patrols properties.
Then create the parent view model
public class AssignPatrolViewModel
{
[Display(Name = "Patrol")]
[Required(ErrorMessage = "Please select a patrol")]
public int? SelectedPatrol { get; set; }
public IEnumerable<SelectListItem> PatrolList { get; set; }
public List<PatrolMemberViewModel> Members { get; set; }
}
and you GET method would be
public ViewResult Unassigned()
{
var model = new AssignPatrolViewModel
{
PatrolList = new SelectList(db.Patrols, "PatrolId", "PatrolName"), // modify to suit
Members = repository.SelectAllUnassigned().ToList()
};
return View(model);
}
and in the view
#model AssignPatrolViewModel
....
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.SelectedPatrol)
#Html.DropDownListFor(m => m.SelectedPatrol, Model.PatrolList, "Please select")
#Html.ValidationMessageFor(m => m.SelectedPatrol)
<table>
.... // thead elements
<tbody>
#for(int i = 0; i < Model.Members.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(m => m.Members[i].IsSelected)
#Html.HiddenFor(m => m.Members[i].MemberId)
// add other hidden inputs for properties you want to post
</td>
<td>#Html.DisplayFor(m => m.Members[i].FirstName)</td>
....
</tr>
}
</tbody>
</table>
<input type="submit" value="Assign" class="btn btn-success" />
}
Then in the POST method
[HttpPost]
public ViewResult Unassigned(AssignPatrolViewModel model)
{
if (!ModelState.IsValid)
{
model.PatrolList = new SelectList(db.Patrols, "PatrolId", "PatrolName");
return View(model);
}
// Get selected ID's
IEnumerable<int> selected = model.Members.Where(m => m.IsSelected).Select(m => m.MemberId);
// Get matching data models
var members = db.Person.Where(p => selected.Contains(p.PID)); // modify to suit
// loop each each member, update its PatrolId to the value of model.SelectedPatrol
// save and redirect
}
You could create a new class for your view model, with the list of users and the list of sections as properties within it. Some people seem to like that approach.
But I think your use of ViewBag for passing the list of sections is perfectly valid. I do that all the time for DDLs like this.

Categories