in my journey of learning ASP.NET MVC I encounterd another difficulty:
I'm trying to POST a form with 3 checkboxes, the checkboxes are looped onto the form according to a bound PresentationModel.
I don't know what to fill in at the "asp-for" tag-helpers for the checkboxes in the view so they pass a boolean to the "Create()" ActionResult in the controller and to show the values in the "Overview" View.
Currently it passes NULL for al of them, the other aproaches I tried always resulted in an "InvalidCastException" as it has to be a boolean not an "int[]".
PresentationModel (PMRegistration.cs)
public class PMRegistration
{
public List<Device> Devices { get; set; }
}
View (Index.cshtml)
#model Week3_oef2_ITPro.PresentationModel.PMRegistration
<form asp-controller="Register" asp-action="Create" method="post">
<table>
<tr>
<td>Are you wearing any dangerous accessoires</td>
</tr>
#foreach (var item in Model.Devices)
{
<tr>
<td>#item.Name</td>
<td class="form-group">
<input type="checkbox" asp-for="#item.CheckState" value="#item.Id" class="form-control" />
</td>
</tr>
}
<tr>
<td>
<input type="submit" class="btn btn-default" />
</td>
</tr>
</table>
</form>
Model (Device.cs)
public class Device
{
public int Id { get; set; }
public string Name { get; set; }
public bool CheckState { get; set; }
}
Model (Data.cs, the Device objects get initialized here)
private static List<Device> devices = new List<Device>();
static Data()
{
devices.Add(new Device() { Id = 1, Name = "Laptop" });
devices.Add(new Device() { Id = 2, Name = "Tablet" });
devices.Add(new Device() { Id = 3, Name = "Apple Watch" });
}
public static List<Device> GetDevices()
{
return devices;
}
Controller (RegisterController.cs)
public class RegisterController : Controller
{
// GET: /<controller>/
[HttpGet]
public IActionResult Index()
{
PMRegistration pm = new PMRegistration();
pm.Devices = Data.GetDevices();
return View(pm);
}
public ActionResult Create(PMRegistration pm)
{
if (ModelState.IsValid)
{
return View("Overview", pm);
}
else
{
return RedirectToAction("Index");
}
}
}
------------ SOLVED -------------
With HTML-helpers:
#for (int i = 0; i < Model.Devices.Count; i++)
{
<tr>
<td>
#Model.Devices[i].Name
</td>
<td>
#Html.CheckBoxFor(m => Model.Devices[i].CheckState)
#Html.HiddenFor(m => Model.Devices[i].Id)
#Html.HiddenFor(m => Model.Devices[i].Name)
</td>
</tr>
}
Related
Could you please help me, To get multiple table data as list from view controller using view model. Below is my Code Its Working till passing multiple model to View. I am not able to get those updated values from View in controller Action Method to Update the data.
I have Two Models.
public class Teacher
{
public int TeacherId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class Student
{
public int StudentId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
Pass those Model in a model as list
public class ViewModel
{
public IEnumerable<Teacher> Teachers { get; set; }
public IEnumerable<Student> Students { get; set; }
}
In Controller, I have define the View Model
[HttpGet]
public ActionResult IndexViewModel()
{
ViewModel mymodel = new ViewModel();
mymodel.Teachers = GetTeachers();
mymodel.Students = GetStudents();
return View(mymodel);
}
In My View, I have all the values correctly but when I change the value same submit. the updated data is not able to get on action method. Please Help me on this.
#using MultipleModelInOneView;
#model ViewModel
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<table>
<tr>
<th>Id</th>
<th>Code</th>
<th>Name</th>
</tr>
#foreach (Teacher teacher in Model.Teachers)
{
<tr>
<td>#Html.DisplayFor(model => #teacher.TeacherId)</td>
<td>#Html.EditorFor(model => #teacher.Code)</td>
<td>#Html.EditorFor(model => #teacher.Name)</td>
</tr>
}
</table>
<p><b>Student List</b></p>
<table>
<tr>
<th>Id</th>
<th>Code</th>
<th>Name</th>
<th>Enrollment No</th>
</tr>
#foreach (Student student in Model.Students)
{
<tr>
<td>#Html.DisplayFor(model => #student.StudentId)</td>
<td>#Html.EditorFor(model => #student.Code)</td>
<td>#Html.EditorFor(model => #student.Name)</td>
</tr>
}
</table>
<input type="submit" value="Save" class="btn btn-primary" />
}
Here I am Not able to Get List from View
[HttpPost]
public ActionResult IndexViewModel("Here I'm not able to Get List from View")
{
return View();
}
try to replace foreach loop by for loop
#for (var i=0; i < Model.Teachers.Count; i+=1)
{
<tr>
<td>#Html.DisplayFor(model =>model.Teachers[i].TeacherId)</td>
<td>#Html.EditorFor(model => model.Teachers[i].Code)</td>
<td>#Html.EditorFor(model => model.Teachers[i].Name)</td>
</tr>
}
#for (var i=0; i < Model.Students.Count; i+=1)
{
<tr>
<td>#Html.DisplayFor(model => model.Students[i].StudentId)</td>
<td>#Html.EditorFor(model => model.Students[i].Code)</td>
<td>#Html.EditorFor(model => model.Students[i].Name)</td>
</tr>
}
and the controller action
public ActionResult IndexViewModel(ViewModel viewModel)
{
....
}
I'm trying to figure out how to bind form elements to model containing a property that is an array or enumerable. This form should be usable for adding a new object or editing an existing one.
Below is a stripped down example.
If Levels contains no elements (e.g. create), the row of Level fields is not rendered. Should you add an empty element to the Level array or should you add a row of empty fields? But how to do this using the asp-for attribute.
If Levels contains elements (e.g. edit), the rows of Level fields is rendered. However, when posting model.Levels in the Edit method is null.
Any ideas how to best implement this?
Model to bind
public class CarparkModel
{
[HiddenInput]
public int Id { get; set; }
public string Name { get; set; }
public Level[] Levels { get; set; }
}
public class Level
{
[HiddenInput]
public int Id { get; set; }
public string Description { get; set; }
public int NrOfSpaces { get; set; }
}
Main view
#model CarparkModel
<form method="POST">
<input asp-for="Id">
<div>
<label asp-for="Name"></label>
<input asp-for="Name">
</div>
<table>
<tr>
<th>Description</th>
<th>Nr of spaces</th>
</tr>
#foreach (Level level in Model.Levels)
{
<tr>
<td><input asp-for="Description"></td>
<td><input asp-for="NrOfSpaces"></td>
</tr>
}
<tr>
<td colspan="2>
<!-- Click = new row of Level fields added -->
<button type="button">Add level</button>
</td>
</tr>
</table>
<button type="submit">Save</button>
</form>
Controller
public class CarparkController
{
[HttpGet]
public IActionResult Create()
{
return View(new CarparkModel());
}
[HttpPost]
public IActionResult Create(CarparkModel model)
{
if (ModelState.IsValid)
{
_repository.Save(model);
return RedirectToAction("index");
}
return View(model);
}
[HttpGet]
public IActionResult Edit(int id)
{
return View(_repository.Get(id));
}
[HttpPost]
public IActionResult Edit(CarparkModel model)
{
if (ModelState.IsValid)
{
_repository.Save(model);
return RedirectToAction("index");
}
return View(model);
}
}
Don't use foreach if you want to be able to send the items back in the form. use for loop with indexed naming for the items.
#model CarparkModel
<form method="POST">
<input asp-for="Id">
<div>
<label asp-for="Name"></label>
<input asp-for="Name">
</div>
<table>
<tr>
<th>Description</th>
<th>Nr of spaces</th>
</tr>
#for(var index = 0, index < Model.Levels.Length, index++)
{
<tr>
<td><input asp-for="#Model.Levels[index].Description"></td>
<td><input asp-for="#Model.Levels[index].NrOfSpaces"></td>
</tr>
}
<tr>
<td colspan="2">
<!-- Click = new row of Level fields added -->
<button type="button">Add level</button>
</td>
</tr>
</table>
<button type="submit">Save</button>
</form>
The index is needed to reconstruct the collection when posting it in the form.
I want to post data to database but my viewmodel is empty when posting it to the controller, i tried different approaches, but everytime my viewmodel is null.
These are my classes :
public class Player
{
[Key]
public int Id { get; set; }
public string Name{ get; set; }
public string PreName{ get; set; }
}
public class Activitity
{
[Key]
public int Id { get; set; }
public string WhichActivity { get; set; }
public List<Player> Players { get; set; }
}
public class Aanwezigheid
{
[Key]
public int Id { get; set; }
public ReasonEnum Reason{ get; set; }
public int PlayerId{ get; set; }
public Player Player{ get; set; }
public List<Player> Players{ get; set; }
public int ActivityId { get; set; }
}
My View Model :
public class PresenceVM
{
public int PlayerId{ get; set; }
public int ActivityId { get; set; }
public string Name{ get; set; }
public string PreName { get; set; }
public ReasonEnum Reason { get; set; }
}
My HTTPGET for a list of players and I want to put the absence reason with the player in the database.
[HttpGet]
public ActionResult Presence(int id)
{
var sp = _context.Players.ToList();
foreach(Players s in sp)
{
var van = new PresenceVM
{
PlayerId = s.Id,
Name = s.Name,
PreName = s.PreName,
ActivityId = id
};
list.Add(van);
}
return View(list);
}
My HttpPost
[HttpPost]
public ActionResult Presence(List<PresenceVM> list)
{
var sp = _context.Players.ToList();
var list = new List<Presence>();
foreach (Players s in sp)
{
var van = new Aanwezigheid
{
PlayerId = s.Id,
ActivityId = vm.ActivityId,
Reason = vm.Reason
};
list.Add(van);
_context.Presence.Add(van);
//_context.SaveChanges();
}
return RedirectToAction("Index", "Presence");
}
The problem is that my PresenceVm (viewmodel) does not get any data in true my controller. I don't understand why? Is it because of a list? With one item it's easy to post it to database. Maybe multiple items?
Edit 1:
The viewmodel for the Get & Post
#model IEnumerable<....ViewModels.PresenceVM>
#{
ViewBag.Title = "Presence";
}
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.PreName)
</th>
<th>
#Html.DisplayName("Reason")
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.PreName)
</td>
<td>
#Html.EnumDropDownListFor(modelItem => item.Reason, "Present", new { #class = "form-control" })
</td>
</tr>
}
</table>
<form action="/Presences/Presence" method="post">
<div class="form-horizontal form-details">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
You're putting the form scope in wrong place (only include submit button). The correct way should include all properties you want to submit with indexes (since you're using IEnumerable<PresenceVM>, like this example:
<form action="/Presences/Presence" method="post">
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.PreName)
</th>
<th>
#Html.DisplayName("Reason")
</th>
</tr>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.EditorFor(modelItem => item[i].Name)
</td>
<td>
#Html.EditorFor(modelItem => item[i].PreName)
</td>
<td>
#Html.EnumDropDownListFor(modelItem => item[i].Reason, "Present", new { #class = "form-control" })
</td>
</tr>
}
</table>
<div class="form-horizontal form-details">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
Note that if you want to allow user input, you need to change all DisplayFor into EditorFor.
Your Presence method is returning an object that does not exist (list).
It must return the view model if you want to use it in the post method.
[HttpGet]
public ActionResult Presence(int id)
{
List<PresenceVM> model = context.Players.Select(u => new PresenceVM
{
PlayerId = s.Id,
Name = s.Name,
PreName = s.PreName,
ActivityId = id
}).ToList();
return View(model);
}
Seems that you don't use the Form tag
VIEW
#model MyViewModel
#using (Html.BeginForm(("Presence"))
{
#Html.AntiForgeryToken()
//divs
<div class="form-horizontal form-details">
<input type="submit" value="Save" class="btn btn-default" />
</div>
}
And it's better to pass a Model than a list of Model
VIEWMODEL
public class MyViewModel{
public IList<PresenceVM> MyList {get;set;}
}
CONTROLLER
public ActionResult Presence(MyViewModel xxx)
{
//whatever
}
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
I am binding objects in a razor foreach in the index.html:
VIEW
#using (Ajax.BeginForm("Save", "Unit", new AjaxOptions { OnSuccess = "onSuccess" }))
{
<button type="submit" class="btn btn-default" id="saveUnits"><i class="fa fa-save"></i></button>
<table>
<tbody>
#foreach (var item in Model)
{
<tr>
#Html.HiddenFor(modelItem => item.UnitId)
<td>
#Html.EditorFor(modelItem => item.Name)
</td>
<td>
#Html.EditorFor(modelItem => item.ErrorText)
</td>
</tr>
}
</tbody>
</table>
}
I have grabbed the data sent to my action parameter with fiddler and got this:
item.UnitId=5&
item.Name=111111111111&
item.ErrorText=fsdddddddddddddddd+&
item.UnitId=5&
item.Name=+&
item.ErrorText=dddddd+&
ACTION
public ActionResult Save(List<Unit> units )
{
return new EmptyResult();
}
VIEWMODEL
public class Unit
{
[HiddenInput(DisplayValue = false)]
public int UnitId { get; set; }
[DataType(DataType.MultilineText)]
public string Name { get; set; }
[DataType(DataType.MultilineText)]
public string ErrorText { get; set;
}
Why is my units instance null? The properties match so they should be bound!
Did I overlook something?
You need to use a for loop not a foreach loop. Also, it would be better to make your Model class have a property which is a collection.
Your model could be something like:
public class UnitsViewModel
{
public List<Unit> Units { get; set; }
public class Unit
{
[HiddenInput(DisplayValue = false)]
public int UnitId { get; set; }
[DataType(DataType.MultilineText)]
public string Name { get; set; }
[DataType(DataType.MultilineText)]
public string ErrorText { get; set; }
}
}
And you could do the following in your cshtml:
#for (int i = 0; i < Model.Count; i++)
{
<tr>
#Html.HiddenFor(m => m.Units[i].UnitId)
<td>
#Html.EditorFor(m => m.Units[i].Name)
</td>
<td>
#Html.EditorFor(m => m.Units[i].ErrorText)
</td>
</tr>
}