Return Viewmodel to HttpPost ActionResult - c#

I'm trying to update my collection of users' roles in my ASP.NET Identity project, but I'm currently stuck because I'm getting a null UsersAndRolesDictionary property in the ViewModel sent to my [HttpPost] method.
Here is my ViewModel, UpdateUserRolesViewModel:
namespace Project_Name.Models
{
public class UpdateUserRolesViewModel
{
public IDictionary<ApplicationUser, ICollection<IdentityUserRole>> UsersAndRolesDictionary { get; set; } // <-- This is returning null currently
}
}
Here's my HomeController's methods:
[Authorize(Roles = "Admin")]
public ActionResult RoleManager()
{
ViewBag.Message = "Role Management Page";
var databaseContext = new ApplicationDbContext(); // Get the Database Context
var users = databaseContext.Users.Include(u => u.Roles); // Get all users from the Database and their Roles
var newDict = new Dictionary<ApplicationUser, ICollection<IdentityUserRole>>();
// Add each user and their roles to the dictionary
foreach (var user in users)
{
newDict.Add(user, user.Roles);
}
// Update the ViewModel with the collection of users and roles
var updateUserRolesViewModel = new UpdateUserRolesViewModel {UsersAndRolesDictionary = newDict};
return View(updateUserRolesViewModel);
}
[HttpPost]
[Authorize(Roles = "Admin")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> UpdateUsersRolesAsync(UpdateUserRolesViewModel updateUserRolesViewModel)
{
try
{
//TODO: Attempt to update the user roles or delete the user
return View("RoleManager");
}
catch
{
//TODO: Properly catch errors
return View("RoleManager");
}
}
Here is my View, RoleManager:
#using Project_Name.Models
#model UpdateUserRolesViewModel
#{
ViewBag.Title = "Role Manager";
var databaseContext = new ApplicationDbContext(); // Get the Database Context
var roles = databaseContext.Roles; // Get all Roles from the database, use this to compare against
}
<h2>#ViewBag.Title</h2>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
#using (Html.BeginForm("UpdateUsersRolesAsync", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-group">
<div class="table-responsive">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Email</th>
<th>Roles</th>
<th>Delete User?</th>
</tr>
</thead>
<tbody>
#{
int i = 0; // Used to make unique IDs for the user's table row, and deleteUserCheckbox
int j = 0; // Used to make unique IDs for the role checkboxes
foreach (var user in Model.UsersAndRolesDictionary.Keys)
{
i++;
<tr id="userTableRow_#i">
<td>#user.Email</td>
<td>
#* Show each role availabe as a checkbox. Check them if the user has that role. *#
#foreach (var role in roles)
{
#Html.CheckBox("userRoleCheckbox_" + j++, user.Roles.Any(identityUserRole => identityUserRole.RoleId.Contains(role.Id)))
<span>#role.Name</span>
<br />
}
</td>
<td>
#Html.CheckBox("deleteUserCheckbox_" + i)
<span>Delete User</span>
</td>
</tr>
}
}
</tbody>
</table>
</div>
#* Reset and Submit buttons *#
<div class="col-lg-2 col-lg-push-8 col-md-2 col-md-push-8 col-sm-2 col-sm-push-8 col-xs-2 col-xs-push-8">
<input type="reset" class="btn btn-danger btn-block" value="Reset" />
</div>
<div class="col-lg-2 col-lg-push-8 col-md-2 col-md-push-8 col-sm-2 col-sm-push-8 col-xs-2 col-xs-push-8">
<input type="submit" class="btn btn-primary btn-block" value="Submit" />
</div>
</div>
}
</div>
</div>
I'm using the dictionary UsersAndRolesDictionary to collect all the users and their roles, then enumerating through that to produce my view in the form of a table.
I'm hoping to change the checkbox values of potential multiple users, then passing that updated ViewModel to my [HttpPost] UpdateUsersRolesAsync method in order to update my user roles, but right now I'm getting a null value for the UsersAndRolesDictionary property and I'm not sure why or how to fix it.
Thanks to Stephen Muecke's links/answers in the comments I was able to answer this question. See my answer post below.

Following the suggestions of Stephen Muecke in the comments, I have gotten a valid ViewModel to be returned.
Added/updated three ViewModels that combine together:
The first being RoleViewModel:
public class RoleViewModel
{
public string Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
Second being UserViewModel:
public class UserViewModel
{
public string Id { get; set; }
public string Email { get; set; }
public List<RoleViewModel> RoleViewModels { get; set; }
public bool DeleteUser { get; set; } // Doesn't work yet, might be in the wrong place
}
And finally the third being an updated version of UpdateUserRoleViewModel:
public class UpdateUserRolesViewModel
{
public int Id { get; set; }
public List<UserViewModel> UserViewModels { get; set; }
}
In my updated HomeController are the methods again:
[Authorize(Roles = "Admin")]
public ActionResult RoleManager()
{
ViewBag.Message = "Role Management Page";
var databaseContext = new ApplicationDbContext(); // Get the Database Context
var users = databaseContext.Users.Include(u => u.Roles).ToList(); // Get all users from the Database and their Roles
// Create the UpdateUserRolesViewModel
var updateUserRolesViewModel = new UpdateUserRolesViewModel
{
Id = 0, // Not sure what else the Id would be
UserViewModels = new List<UserViewModel>()
};
// Add each user to the UserViewModels list
for (int i = 0; i < users.Count(); i++)
{
var userViewModel = new UserViewModel
{
Id = users.AsEnumerable().ElementAt(i).Id,
Email = users.AsEnumerable().ElementAt(i).UserName,
RoleViewModels = new List<RoleViewModel>(),
DeleteUser = false
};
// Add each role from the Roles table to the RoleViewModels list, check if user has that role
foreach (var role in databaseContext.Roles)
{
var roleViewModel = new RoleViewModel
{
Id = role.Id,
Name = role.Name,
IsSelected = users.AsEnumerable().ElementAt(i).Roles.Any(identityUserRole => identityUserRole.RoleId.Contains(role.Id))
};
userViewModel.RoleViewModels.Add(roleViewModel);
}
updateUserRolesViewModel.UserViewModels.Add(userViewModel);
}
return View(updateUserRolesViewModel);
}
[HttpPost]
[Authorize(Roles = "Admin")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> UpdateUsersRolesAsync(UpdateUserRolesViewModel updateUserRolesViewModel)
{
try
{
// Attempt to update the user roles
foreach (var user in updateUserRolesViewModel.UserViewModels)
{
// Delete user
//TODO: Prompt user to confirm deletion if one or more people are being deleted
if (user.DeleteUser)
{
var userToDelete = await UserManager.FindByIdAsync(user.Id); // Get the ApplicationUser object of who we want to delete
await UserManager.DeleteAsync(userToDelete); // Delete the user
continue; // Don't try to update the roles of a deleted user.
}
// Remove all roles from the User
var rolesToRemove = await UserManager.GetRolesAsync(user.Id);
await UserManager.RemoveFromRolesAsync(user.Id, rolesToRemove.ToArray());
// Add roles to the User
foreach (var roleViewModel in user.RoleViewModels.Where(r => r.IsSelected))
{
await UserManager.AddToRoleAsync(user.Id, roleViewModel.Name);
}
}
return RedirectToAction("RoleManager");
}
catch
{
//TODO: Properly catch errors
return RedirectToAction("RoleManager");
}
}
Finally, here is my View, RoleManager
#using Project_Name.ViewModels
#model UpdateUserRolesViewModel
#{
ViewBag.Title = "Role Manager";
}
#* Debugging text *#
#foreach (var user in Model.UserViewModels)
{
<div>User ID: #user.Id</div>
<div>User Name: #user.Email</div>
<p>
#foreach (var roleViewModel in user.RoleViewModels.Where(r => r.IsSelected))
{
<div>Role ID: #roleViewModel.Id</div>
<div>Role Name: #roleViewModel.Name</div>
}
</p>
<hr />
}
<h2>#ViewBag.Title</h2>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
#using (Html.BeginForm("UpdateUsersRolesAsync", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
<div class="form-group">
<div class="table-responsive">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Email</th>
<th>Roles</th>
<th>Delete User?</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.UserViewModels.Count; i++)
{
<tr id="userTableRow_#i">
<td>
#Html.HiddenFor(m => m.UserViewModels[i].Id)
#Html.HiddenFor(m => m.UserViewModels[i].Email)
#Model.UserViewModels[i].Email
</td>
<td>
#for (int j = 0; j < Model.UserViewModels[i].RoleViewModels.Count; j++)
{
#Html.HiddenFor(m => m.UserViewModels[i].RoleViewModels[j].Id)
#Html.HiddenFor(m => m.UserViewModels[i].RoleViewModels[j].Name)
#Html.CheckBoxFor(m => m.UserViewModels[i].RoleViewModels[j].IsSelected)
#Html.DisplayTextFor(m => m.UserViewModels[i].RoleViewModels[j].Name)
<br/>
}
</td>
<td>
#Html.CheckBoxFor(m => m.UserViewModels[i].DeleteUser)
#Html.DisplayNameFor(m => m.UserViewModels[i].DeleteUser)
</td>
</tr>
}
</tbody>
</table>
</div>
#* Reset and Submit buttons *#
<div class="col-lg-2 col-lg-push-8 col-md-2 col-md-push-8 col-sm-2 col-sm-push-8 col-xs-2 col-xs-push-8">
<input type="reset" class="btn btn-danger btn-block" value="Reset" />
</div>
<div class="col-lg-2 col-lg-push-8 col-md-2 col-md-push-8 col-sm-2 col-sm-push-8 col-xs-2 col-xs-push-8">
<input type="submit" class="btn btn-primary btn-block" value="Submit" />
</div>
</div>
}
</div>
</div>
This now updates the user's Roles, and Deletes them (though there is no confirmation check so be careful with that!)

Related

Access view model data in controller

In my application I have created a view model to show data in the View.
I created view model as this.
[NotMapped]
public class EmpExcelUploadViewModel {
public int CompanyId {
get;
set;
}
public int EmpNo {
get;
set;
}
public string EmpName {
get;
set;
}
public int EmpDep {
get;
set;
}
public int EmpDes {
get;
set;
}
}
In the controller I have assigned data to the view model and passed to the view. Just to show the data to the user, there is no editing or deleting data from the user view. That stage is works fine.
This is the view
#model IEnumerable
<Asp_PASMVC.ViewModels.EmpExcelUploadViewModel>
#{
ViewBag.Title = "Import";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div>
#using (Html.BeginForm("Import", "M_Employee", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="card card-primary">
<div class="card-header">
<h1 class="card-title"><b>Upload Employees from Excel</b></h1>
</div>
<div>
<br />
#Html.Raw(ViewBag.Error)
<h4><span>Select Excel File</span></h4>
<input type="file" name="excelFile" class="btn btn-warning" />
<br />
</div>
<div class="row">
<div class="col-md-1">
<br />
<br />
<input type="submit" value="Import" class="btn btn-info" />
</div>
<div class="col-md-1">
<br />
<br />
<input type="button" value="Upload" class="btn btn-success" onclick="location.href='#Url.Action("UploadEmployees", "M_Employee")'" />
</div>
</div>
<div class="card-body p-0">
<table class="table table-striped">
<tr>
<th>Company</th>
<th>EmpId</th>
<th>EmpName</th>
<th>Department</th>
<th>Dessignation</th>
</tr>
#if (ViewBag.EmpList != null)
{
foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.CompanyId)
</td>
<td>
#Html.DisplayFor(modelItem => item.EmpNo)
</td>
<td>
#Html.DisplayFor(modelItem => item.EmpName)
</td>
<td>
#Html.DisplayFor(modelItem => item.EmpDep)
</td>
<td>
#Html.DisplayFor(modelItem => item.EmpDes)
</td>
</tr>
}
}
</table>
</div>
<!-- /.card-body -->
</div>
}
</div>
I want to know, when user clicks to the Upload button, that the data already in the view model I want to pass to the database. So how can I access the data already in the view model? I'm still learning MVC and I don't know if it's possible or not.
This is what I tried. Here in the empExcel getting error that EmpExcelUploadViewModel does not contain public instance.
public ActionResult UploadEmployees(EmpExcelUploadViewModel empExcel) {
try {
foreach(var item in empExcel) // error in empExcel {
int ComId = item.CompanyId;
int EmpNo = item.EmpNo;
string EmpName = item.EmpName;
int DepId = item.EmpDep;
int DesId = item.EmpDes;
var isEmployeeExists = (from e in db.CreateEmployee where e.EmpNo == EmpNo select e.Id).First();
if (isEmployeeExists != 0) {
M_Employee e = new M_Employee();
e.CompanyId = ComId;
e.EmpNo = EmpNo;
e.EmpName = EmpName;
e.DepId = DepId;
e.DesignId = DesId;
e.Status = true;
e.CreateDate = DateTime.Now;
e.CreateBy = int.Parse(((System.Security.Claims.ClaimsIdentity) User.Identity).FindFirst("UserId").Value);
db.CreateEmployee.Add(e);
db.SaveChanges();
} else {
M_Employee m_Employee = db.CreateEmployee.First(x => x.Id == isEmployeeExists);
m_Employee.DepId = DepId;
m_Employee.DesignId = DesId;
db.SaveChanges();
}
}
} catch (Exception ex) {
ViewBag.Error = "Error " + ex;
}
ViewBag.Error = "All Records uploaded to the database";
ViewBag.EmpList = null;
return View("Import");
}
}
Instead of saving data in the view, you should do it in the controller.
In the controller, if there's any error saving the data to the database, set a separate error flag indicating this. Then, the View uses this flag to determine what to display

How can i fill a table with values from a dropdownlist in ASP.NET Core MVC

I want to fill an html table with the selecteditem from a dropdownlist, I already passed the database data to this list, and I also get all the information of the selected value through an actionresult, so the thing I want is when I select an item, put it in the table, and do this every time I want so I can add various values at the same time. does anyone have any idea how to do it?
This is my model
public partial class Producto
{
public int Id { get; set; }
public string Nombre { get; set; }
public string Descripcion { get; set; }
public string Precio { get; set; }
[NotMapped]
public List <Producto> Listasnom { get; set; }
[NotMapped]
public IEnumerable < SelectListItem > ProductoListItems
{
get
{
return new SelectList(Listasnom ?? new List<Producto>(), "Id", "Nombre");
}
}
}
My controller
MyDbContext db = new MyDbContext();
[HttpGet]
public ActionResult Index()
{
return View(db.Pedido.ToList());
}
public ActionResult Create()
{
var lista = new Producto();
lista.Listasnom = db.Producto.ToList();
return View(lista);
}
[HttpPost]
public ActionResult Create(Producto producto)
{
var lista = new Producto();
lista.Listasnom = db.Producto.ToList();
var emp = lista.Listasnom.Where(e => e.Id == producto.Id).FirstOrDefault();
lista.Id = emp.Id;
lista.Nombre = emp.Nombre;
lista.Precio = emp.Precio;
return View(lista);
}
and my view:
#model punto.Models.Producto
<form class="form-horizontal" method="post" action="">
<div class="box-body">
<div class="form-group">
<label for="inputEmail3" class="col-sm-1 control-label">Producto</label>
<div class="col-sm-10">
#Html.DropDownListFor(model => model.Id, Model.ProductoListItems, "Elegir Producto", new { #class = "form-control"})
</div>
<button type="submit" class="btn btn-info">Agregar</button>
</div>
</form>
This is the table where I want to put the values
<table id="example1" class="table table-bordered table-responsive">
<thead>
<tr>
<th>Id</th>
<th>Producto</th>
<th>Precio</th>
</tr>
</thead>
<tbody>
<tr>
<td>#Html.DisplayFor(model => model.Id)</td>
<td>#Html.DisplayFor(model => model.Nombre)</td>
<td>#Html.DisplayFor(model => model.Precio) </td>
</tr>
</table>
I don't have any idea how to do it, if someone can help me.
If you want to select more products and store them in the table , you could try to use ajax as Nhien said :
View :
#model WebApplication2.Models.Producto
<div class="box-body">
<div class="form-group">
<label for="inputEmail3" class="col-sm-1 control-label">Producto</label>
<div class="col-sm-10">
#Html.DropDownListFor(model => model.Id, Model.ProductoListItems, "Elegir Producto", new { #class = "form-control",id="selectlist" })
</div>
<button type="submit" id="btnCreate" class="btn btn-info">Agregar</button>
</div>
</div>
<table id="example1" class="table table-bordered table-responsive">
<thead>
<tr>
<th>Id</th>
<th>Producto</th>
<th>Precio</th>
</tr>
</thead>
<tbody> </tbody>
</table>
#section Scripts
{
<script type="text/javascript">
$("#selectlist").change(function () {
var id = $("#selectlist").val()
$.ajax({
type: "get",
url: "/products/getproduct?id=" + id,
success: function (data) {
var tBody = $("#example1 > TBODY")[0];
var row = tBody.insertRow(-1);
//Add ID cell.
var cell = $(row.insertCell(0));
cell.html(data.id);
//Add Nombre cell.
cell = $(row.insertCell(1));
cell.html(data.nombre);
//Add Precio cell.
cell = $(row.insertCell(2));
cell.html(data.precio);
}
});
});
</script>
}
Controller :
public IActionResult GetProduct(int id)
{
var selectProduct = db.Producto.Find(id);
return Json(selectProduct);
}

ASP.NET Core 2.1 Edit w/ Concatenated Primary Key

I'm a student using ASP.NET Core and related technologies for the first time. I've spent hours searching for a solution to this problem with no luck, so if anyone could help or point me at a tutorial to fix this, I would be very grateful.
My group and I are trying to build a web app to interface with an existing SQL database which has a lot of tables using concatenated primary keys. We can't change that. I used this tutorial to get our CRUD edit pages to even show, but if I try to submit any changes via that Edit page, I get an error like this:
No webpage was found for the web address: https://localhost:44311/GenCollectionSamplers/Edit
After some debugging, we're pretty sure this is a problem with the POST Edit method in the Controller getting null IDs, but we cannot figure out how to pass the correct IDs into the POST Edit method. The IDs are present in the GET Edit message.
Let me know if more code is needed.
Edit GET and POST from Controller:
// GET: GenCollectionSamplers/Edit/5
public async Task<IActionResult> Edit(double? peopleID, double? colID)
{
if (peopleID == null || colID == null)
{
return NotFound();
}
var genCollectionSamplers = await _context.GenCollectionSamplers.FindAsync(peopleID, colID);
if (genCollectionSamplers == null)
{
return NotFound();
}
ViewData["FkPeopleId"] = new SelectList(_context.GenPeople, "PkPeopleId", "PkPeopleId", genCollectionSamplers.FkPeopleId);
ViewData["FkCollectionId"] = new SelectList(_context.GenCollections, "CollectionId", "CollectionId", genCollectionSamplers.FkCollectionId);
return View(genCollectionSamplers);
}
// POST: GenCollectionSamplers/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(double peopleID, double colID, [Bind("FkPeopleId,FkCollectionId")] GenCollectionSamplers genCollectionSamplers)
{
//Causes error because peopleID and colID are 0
if (peopleID != genCollectionSamplers.FkPeopleId || colID != genCollectionSamplers.FkCollectionId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(genCollectionSamplers);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!GenCollectionSamplersExists(genCollectionSamplers.FkPeopleId) || !GenCollectionSamplersExists(genCollectionSamplers.FkCollectionId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["FkPeopleId"] = new SelectList(_context.GenPeople, "PkPeopleId", "PkPeopleId", genCollectionSamplers.FkPeopleId);
ViewData["FkCollectionId"] = new SelectList(_context.GenCollections, "CollectionId", "CollectionId", genCollectionSamplers.FkCollectionId);
return View(genCollectionSamplers);
}
Model:
public partial class GenCollectionSamplers
{
[Required]
[Display(Name = "Fk People Id")]
public double FkPeopleId { get; set; }
[Required]
[Display(Name = "Fk Collection Id")]
public double FkCollectionId { get; set; }
[Required]
[Display(Name = "Fk Collection")]
public GenCollections FkCollection { get; set; }
[Required]
[Display(Name = "Fk People")]
public GenPeople FkPeople { get; set; }
}
Edit view:
#model {path removed}.Models.GenCollectionSamplers
#{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
<h4>GenCollectionSamplers</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="FkCollectionId" class="control-label"></label>
<input asp-for="FkCollectionId" class="form-control" />
<span asp-validation-for="FkCollectionId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FkPeopleId" class="control-label"></label>
<input asp-for="FkPeopleId" class="form-control" />
<span asp-validation-for="FkPeopleId" class="text-danger"></span>
</div>
<input type="hidden" asp-for="FkPeopleId" />
<input type="hidden" asp-for="FkCollectionId" />
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Part of Index view:
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => efModel.FkCollectionId)
</th>
<th>
#Html.DisplayNameFor(model => efModel.FkPeople)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FkCollection.CollectionId)
</td>
<td>
#Html.DisplayFor(modelItem => item.FkPeople.PkPeopleId)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { peopleID = item.FkPeopleId, colID = item.FkCollectionId }) |
#Html.ActionLink("Details", "Details", new { peopleID = item.FkPeopleId, colID = item.FkCollectionId }) |
#Html.ActionLink("Delete", "Delete", new { peopleID = item.FkPeopleId, colID = item.FkCollectionId })
</td>
</tr>
}
</tbody>
</table>
Why do you have the hidden fields on your Edit view:?
<input type="hidden" asp-for="FkPeopleId" />
<input type="hidden" asp-for="FkCollectionId" /
It is this that gets bound to the Model when you submit the HttpPost and since they do not have any values assigned, you are getting 0. Get rid of these two hidden fields. The rendered page will create the hidden fields at run-time (inspect your rendered markup for Edit page) along with __RequestVerificationToken that knows which instance of this Model should be validated and then updated.

Unable to pass value to controller from view through viewmodel

I'm pretty sure my brain is friend and this is something I'm going to laugh at tomorrow morning, but unfortunately I'm stuck on this portion and am asking for assistance.
I have a ViewModel:
public class HousingDetailsViewModel : AppViewModel
{
DataContext db = new DataContext();
public List<string> units { get; set; }
public List<AvailableHousing> availableHousings { get; set; }
public Person person { get; set; }
public OccupiedHousing currentHousing { get; set; }
public OccupiedHousing newHousing;
public HousingDetailsViewModel(int? id)
{
units = db.AvailableHousings.OrderBy(ah => ah.Unit).Select(h => h.Unit).Distinct().ToList();
availableHousings = db.AvailableHousings.Where(h => h.Available == true).OrderBy(h => h.Bed)
.OrderBy(h => h.Room).ToList();
currentHousing = db.OccupiedHousing.Include(o => o.AvailableHousing)
.Include(o => o.Person).Where(o => o.PersonID == id && o.CurrentHousing == true).FirstOrDefault();
person = db.Persons.Find(id);
newHousing = new OccupiedHousing();
}
}
My controller methods for this view:
public ActionResult Details(int? id)
{
return View(new HousingDetailsViewModel(id));
}
[HttpPost]
public ActionResult Move(OccupiedHousing newHousing, int? personID)
{
newHousing.PersonID = personID;
newHousing.DateArrived = DateTime.Now;
newHousing.CurrentHousing = true;
newHousing.AvailableHousingID = housingID;
db.OccupiedHousings.Add(newHousing);
db.SaveChanges();
return RedirectToAction("Index", new HousingViewModel());
}
And my form works fine for all of my fields except for 1, and that's the AvailableHousingID. I've tried setting a hidden value. I put a breakpoint where I set the value of the hidden field and I watched it change, but it didn't make it to the controller. So I changed it to a form submission and tried to catch it as a routevalue and that didn't work either. I'm at a loss, can anyone see where I'm going wrong?
EDIT: Adding View
#model AppName.ViewModels.HousingDetailsViewModel
#{
ViewBag.Title = "Housing Details";
}
#Html.BeginForm("Move", "Housing", new { personID = #Model.person.ID }, FormMethod.Post, new { })
<script>
function setID(id) {
$('#HiddenHousingID').val(id);
$('#HiddenSubmit').click();
}
</script>
<h2>Housing Details</h2>
<div class="row">
<div class="col-xs-12 container">
<div class="col-xs-5">
<img src="//placehold.it/150x200/77CCDD/66BBCC" class="img-responsive" />
</div>
<div class="col-xs-7">
<h4>#Model.person.ComboName</h4>
<h4>#Model.currentHousing.AvailableHousing.Unit - #Model.currentHousing.AvailableHousing.Room - #Model.currentHousing.AvailableHousing.Bed</h4>
<h4>#Model.person.DateOfBirth.Value.ToShortDateString()</h4>
#Html.HiddenFor(m => m.newHousing.AvailableHousingID, new { id = "HiddenHousingID", name = "newHousing.AvailableHousingID")}
</div>
</div>
</div>
<div class="row">
#foreach (var unit in Model.units)
{
<div class="col-xs-6">
<div class="panel panel-primary">
<div class="panel-heading">
<span class="panel-title">
#unit
</span>
</div>
<div class="panel-body">
<table id="MoveHousingTable" class="table table table-condensed table-striped">
<thead>
<tr>
<th>
Available Housing
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var housing in Model.availableHousings.Where(h => h.Unit == unit))
{
<tr>
<td>
#housing.Room -
#housing.Bed
</td>
<td>
<input type="button" value="Select" name="select" onclick="setID(#housing.ID)" />
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
</div>
<input type="submit" class="hidden" id="HiddenSubmit">
}
For the route :
#Html.BeginForm("Move", "Housing", new { personID = #Model.person.ID , housingID= #Model.newHousing.AvailableHousingID}, FormMethod.Post, new { })

Html.BeginForm passing Model item value

Am using mvc4 and am calling another controller in my view using Html.BeginForm
It work fine!but here am using textbox to pass the value.
How to modify this code so am using
#Html.DisplayFor(modelItem => item.UserName)
....instead of
#Html.TextBox("UserName")
here my view :
image of it:
#using OTMS.Models
#model IEnumerable<OTMS.Models.UserProfile>
#{
ViewBag.Title = "Index";
}
<!-- Table Continer -->
<div class="spacer_10px"></div>
<div class="container clearfix">
<div class="grid_12">
<div class="table_wrapper table_gray">
<table>
<tr>
<th>
<p>User Name</p>
</th>
<th>
<p>Role</p>
</th>
<th>
<p>Role</p>
</th>
</tr>
#if (Model != null) {
foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#using(Html.BeginForm("GetRoles", "Account",FormMethod.Post)){
#Html.AntiForgeryToken()
<div class="editor-label">Username : </div>
#Html.TextBox("UserName") //here user will enter user name / I dont want user to enter that ,it should be done Automatically
<div class="spacer_20px"></div>
<div class="button button-orange"> <span class=" form_button clearfix">
<input type="submit" class="submit" name="submit" value="Get Roles for this User" />
</span> </div>//by clicking that will pass the user name to controller (GerRole)/I dont want button
}
</td>
<td>
#using (Html.BeginForm("Submit", "Account", FormMethod.Post))
{
#Html.Hidden("userName", item.UserName)
#Html.DropDownList("selectedRole", (SelectList)ViewBag.Roles)
<div class="button button-orange"> <span class=" form_button clearfix">
<input type="submit" class="submit" name="submit" value="Update Change" />
</span> </div>
}
</td>
</tr>
}
}
</table>
</div> </div>
here my controller :
public ActionResult Index()
{
var model = _db.UserProfiles.ToList();
ViewBag.Roles = new SelectList(Roles.GetAllRoles());
return View(model);
}
[HttpPost]
public ActionResult GetRoles(string UserName)
{
if (!string.IsNullOrWhiteSpace(UserName))
{
ViewBag.RolesForThisUser = Roles.GetRolesForUser(UserName);
SelectList list = new SelectList(Roles.GetAllRoles());
ViewBag.Roles = list;
}
return View("showrole");
}
another view:
image of it :
#{
ViewBag.Title = "showrole";
}
<h2>showrole</h2>
#if(ViewBag.RolesForThisUser != null) {
<text>
<h3>Roles for this user </h3>
<ol>
#foreach (string s in ViewBag.RolesForThisUser){
<li>#s</li>
}
</ol>
</text>
}
What you definetely need to do is to create a view model for your view, for me it looks something like this:
public class UserViewModel
{
public string UserName {get;set;}
public IEnumerable<string> UserRoles { get; set; }
}
Then in your index action you would return a list of these view models.
You certainly could do it like this:
public ActionResult Index()
{
var model = _db.UserProfiles.ToList()
.Select(u => new UserViewModel{
UserName = u.UserName,
UserRoles = Roles.GetRolesForUser(u.UserName)
.AsEnumerable()
})
.ToList();
ViewBag.Roles = new SelectList(Roles.GetAllRoles());
return View(model);
}
but I wouldn't. It's because with this code you're doing one aditional query for every user just to get his roles. I think you need to add roles table to your EntityFramework model and try to do this with single query. So you need to extend your UserProfile with roles:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public ICollection<UserRoles> UserRoles { get; set; }
}
[Table("webpages_Roles")]
public class UserRoles
{
[Key]
public int RoleId { get; set; }
public string RoleName { get; set; }
public ICollection<UserProfile> UserProfiles { get; set; }
}
Then update your DbContext with info about many to many relationship between UserProfils and UserRoles:
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<UserRoles>()
.HasMany<UserProfile>(r => r.UserProfiles)
.WithMany(u => u.UserRoles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
}
}
After that in your index action - you can simply do:
var model = _db.UserProfiles.Select(u => new UserViewModel()
{
UserName = u.UserName,
UserRoles = u.UserRoles.Select(ur=>ur.RoleName)
}).ToList();
And it will be one query instead of several in cycle.
EDIT:
Your model changed so you need to change #model IEnumerable<OTMS.Models.UserProfile> to #model IEnumerable<OTMS.Models.UserViewModel>
And then:
#foreach(var user in Model)
{
//display user
#foreach(var role in user.UserRoles)
{
//display roles with #role
}
}
If you want to use DisplayTemplates - you can move logic for displying user into template. For this you need to create view by path
~/Views/Shared/DisplayTemplates/UserViewModel.cshtml
#model OTMS.Models.UserViewModel
//display user
#foreach(var role in user.UserRoles)
{
//display roles with #role
}
then in Index.cshtml you can change code to this:
#foreach (var user in Model)
{
#Html.DisplayFor(n => user)
}
First and for All ,The Main Confusion is with this Helper..have a Breif Look here
Use HiddenFor when you want to provide posted data that the user does not need to be aware of."
Use DisplayFor when you want to show records but not allow them to be editted.
Use TextBoxFor when you want to allow user input or allow the user to edit a field.
`
Now your question is Like this..
How can i use displayfor to hit my controller!!!!
You could accomplish this with a duo of HiddenFor and DisplayFor. Use HiddenFor to have the values ready to be posted, and DisplayFor to show those values.
so to meet your Requirement
<div class="editor-label"> Username : </div>
#Html.TextBox("UserName")
Replace
<div class="editor-label"> Username : </div>
#Html.HiddenFor(modelItem=>item.username)
#Html.DisplayFor(modelItem=>item.username)
Remember Displayfor Renders Only Label In the Browser,to post it back to Controller you need HiddenFor
Try this:
Controller
[HttpPost]
public ActionResult GetRoles(string UserName)
{
if (!string.IsNullOrWhiteSpace(UserName))
{
ViewBag.RolesForThisUser = Roles.GetRolesForUser(UserName);
SelectList list = new SelectList(Roles.GetAllRoles());
ViewBag.Roles = list;
}
return View("......");
}
View
#ViewBag.Name
#using(Html.BeginForm("GetRoles", "Account")){
#Html.AntiForgeryToken()
<div class="editor-label">Username : </div>
#Html.TextBox("UserName")
<div class="spacer_20px"></div>
<div class="button button-orange">
<span class=" form_button clearfix">
<input type="submit" class="submit" name="submit" value="Get Roles for this User" />
</span>
</div>
}
Here is the DEMO

Categories