How to check user roles in View MVC5 - c#

As the question says, I am trying to check for a user object's roles in MVC5 View. Basically what my view does is a listing of all the registered users and their current role.
Here's my view:
#model IEnumerable<IconicMx.CandidateDatabase.Models.ApplicationUser>
#{
ViewBag.Title = "Talent Center Institute: Usuarios";
ViewBag.Menu = "Usuarios";
ViewBag.Submenu = "Lista de usuarios";
ViewBag.MenuFaClass = "fa-cog";
ViewBag.InitialPanel = true;
}
<h2>Lista de usuarios</h2>
<p>
#Html.ActionLink("Crear usuario", "Register", "Account", null, new { #class = "btn btn-primary" })
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.name)
</th>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>
Tipo de usuario
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#if (ViewBag.manager.IsInRole(item.Id, "Admin"))
{
<text>Administrador</text>
}
else
{
<text>Capturista</text>
}
</td>
<td>
#Html.ActionLink("Editar", "Edit", new { id=item.Id }, new { #class = "btn btn-primary btn-xs" }) |
#Html.ActionLink("Eliminar", "Delete", new { id=item.Id }, new { #class = "btn btn-primary btn-xs" })
</td>
</tr>
}
</table>
And here's my controller:
public ActionResult Index()
{
ViewBag.manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>());
return View(db.Users.ToList());
}
I am trying to use the UserManager's IsInRole function but a runtime error appears when rendering the view saying that function is not defined!! If I invoke this function in the controller it runs as expected.
NOTE: I am NOT trying to get the current logged in User in the session!! (User.identity.IsInRole doesn't help)

First of all, get your data out of database in controller and pass data into your view, not references to service classes. This will save you a lot of headaches. ViewBag is good for passing an odd serialiseable object, but not so good in passing references to classes that have a lot of behaviour.
Second, you need to use IsInRoleAsync method on UserManager. Yes, there is IsInRole non-async extension method available, but I don't see you having a required namespace used.
So I'd do a view model class:
public class UserViewModel
{
public String Id { get; set; }
public String Username { get; set; }
public String Email { get; set; }
public bool IsAdmin { get; set; }
// other properties
}
Then adjust your controller to do the data extraction:
private UserManager<ApplicationUser> userManager;
public MyController()
{
userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>());
}
public async Task<ActionResult> Index()
{
var viewModels = db.Users.Select(u => new UserViewModel(){ Id = u.Id, Username = u.Username, Email = u.Email }).ToList();
foreach (var userModel in viewModels)
{
userModel.IsAdmin = await userManager.IsInRoleAsync(userModel.Id, "Admin");
}
return View(viewModels);
}
Then adjust your view to take a List<UserViewModel> and present the data accordingly - I'll leave that to you to finish.

Related

ASP.NET MVC Complex Object property stays null on form submit

I'm getting myself acquainted with ASP.NET MVC but i'm running into something probably trivial. I have a model called ToDoList, this is a complex type with a list of ToDoItems:
public class ToDoList
{
public Guid Id {get;set;}
public string Name { get; set; }
public virtual ICollection<ToDoItem> Items {get;set;}
}
public class ToDoItem
{
public int Id { get; set; }
public string Task { get; set; }
public bool IsDone { get; set; }
public virtual ToDoList ToDoList { get; set; }
}
My Details page with form looks like this:
#model DataLayer.TomTest.Entities.ToDoList
<h2>#Model.Name</h2>
#using (#Html.BeginForm())
{
#Html.AntiForgeryToken()
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Items.First().Id)
</th>
<th>
#Html.DisplayNameFor(model => model.Items.First().Task)
</th>
<th>
#Html.DisplayNameFor(model => model.Items.First().IsDone)
</th>
</tr>
#foreach (var toDoItem in Model.Items)
{
<tr>
<td>
#toDoItem.Id
</td>
<td>
#Html.EditorFor(model => toDoItem.Task)
</td>
<td>
#Html.EditorFor(model => toDoItem.IsDone, new {htmlAttributes = new {#Style = "margin-left: 10px;"}})
</td>
</tr>
}
</table>
<input type="submit" value="Save" class="btn btn-default"/>
}
And this is the method it posts to:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Details([Bind(Include = "Id,Name,Items")] ToDoList todoList)
{
if (ModelState.IsValid)
{
_context.Entry(todoList).State = EntityState.Modified;
await _context.SaveChangesAsync();
return View();
}
return View();
}
As you can see I included the [Bind] attribute as I read somewhere that would ensure i get the correct properties passed. When I debug this however, only the Id property is filled the rest remains null.
What can I do to fix this? Is it a mistake in the View? Or is it possible Entity Framework isn't setup correctly?
Thanks in advance for your help.
Model binding to a list doesn't work with a foreach; you need to use a for loop instead.
You'll also need hidden inputs for any properties which don't have editors within the loop.
#for (int index = 0; index < Model.Items.Count; index++)
{
<tr>
<td>
#Html.HiddenFor(m => m.Items[index].Id)
#Model.Items[index].Id
</td>
<td>
#Html.EditorFor(m => m.Items[index].Task)
</td>
<td>
#Html.EditorFor(m => m.Items[index].IsDone, new { htmlAttributes = new { #Style = "margin-left: 10px;" } })
</td>
</tr>
}
ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries - Scott Hanselman's Blog

How do I display a list from SQL database when my table is not enumerable? I can use details view fine, but not an index view

I am having difficulty getting my Index view to display the values I have from the SQL database.
The main issue is that I cannot use #foreach (var item in Model) {... because my table is not created as Enumerable (I think). I run into the error message System.NullReferenceException: 'Object reference not set to an instance of an object.' pointing at Model in the foreach statement expression.
I am wondering if I need to set my table up as an Enumerable list, then display each item. Or if I am missing something with regards to my Index view. Or maybe I need to pass something through the Index return View()?
Here is the Image Model:
namespace Uploadimage.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web;
public partial class Image
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string City { get; set; }
public string Province { get; set; }
public string Phone { get; set; }
[Required]
[RegularExpression(#"[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
ErrorMessage = "Email doesn't look like a valid email address.")]
public string Email { get; set; }
[Compare("Email", ErrorMessage = "Emails do not match.")]
public string ConfirmEmail { get; set; }
[DisplayName("Upload File")]
public string ImagePath { get; set; }
public HttpPostedFileBase ImageFile { get; set; }
}
}
Here is the controller:
public class ImageController : Controller
{
[HttpGet]
public ActionResult Add()
{
return View();
}
[HttpPost]
public ActionResult Add(Image imageModel)
{
string fileName = Path.GetFileNameWithoutExtension(imageModel.ImageFile.FileName);
string extension = Path.GetExtension(imageModel.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
imageModel.ImagePath = "~/Image/" + fileName;
fileName = Path.Combine(Server.MapPath("~/Image/"), fileName);
imageModel.ImageFile.SaveAs(fileName);
using(LoginDBEntities db = new LoginDBEntities())
{
db.Images.Add(imageModel);
db.SaveChanges();
}
ModelState.Clear();
return View();
}
[HttpGet]
public ActionResult View(int id)
{
Image imageModel = new Image();
using (LoginDBEntities db = new LoginDBEntities())
{
imageModel = db.Images.Where(x => x.Id == id).FirstOrDefault();
}
return View(imageModel);
}
public ActionResult Index()
{
return View();
}
}
And lastly, my Index View:
#model IEnumerable<Uploadimage.Models.Image>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.City)
</th>
<th>
#Html.DisplayNameFor(model => model.Province)
</th>
<th>
#Html.DisplayNameFor(model => model.Phone)
</th>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>
#Html.DisplayNameFor(model => model.ImagePath)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.Province)
</td>
<td>
#Html.DisplayFor(modelItem => item.Phone)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.ImagePath)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
I have tried thoroughly looking this up but have found things that don't specifically apply to me.
Or I don't know what to look up.
Two things:
Your Index controller action will need to load the images to pass to the view. At a minimum to serve a collection of images as the view's "model":
public ActionResult Index()
{
using (LoginDBEntities db = new LoginDBEntities())
{
var images = db.Images.ToList();
return View(images);
}
}
Then in the view you will need to check whether you actually get any results, then extract your labels from the first result if there are any, or display a suitable message if there are no images:
<!-- ... -->
#if (Model.Any())
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).LastName)
</th>
...
</tr>
#for (int count = 0; count < Model.Count; count++) {
<tr>
<td>
#Html.DisplayFor(model => model.ElementAt(count).FirstName)
</td>
<td>
#Html.DisplayFor(model => model.ElementAt(count).LastName)
</td>
...
<td>
#Html.ActionLink("Edit", "Edit", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Details", "Details", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Delete", "Delete", new { id=#Model.ElementAt(count).Id })
</td>
</tr>
}
</table>
}
else
{
<p> No Images. </p>
}
The above is written from memory so it will likely have some syntactic issues but it should point you in the right direction.
Typically when working with collections like search results I define a view model class for the results page that itself contains the collection of results. The page's model becomes that wrapper view model which can contain details about the page (such as things like current search criteria, lookup values for searches, etc.) along with the collection of results. (typically a PagedList to support pagination)
I.e.
[Serializable]
public class ImageIndexViewModel
{
public string NameSearchString { get; set; }
public ICollection<ImageViewModel> Results { get; set; } = new List<ImageViewModel>();
}
Where ImageViewModel is a serializable POCO view model to represent just the details about images that the view will display. Often the views don't need everything about a data row, and in cases where the data row has navigation properties and extra fields we don't need to display, serializing entities results in lazy load calls or simply sending a lot of extra data that isn't needed.
You don't have anything in your Index method which gets a list of entities.
Your Index method in your controller should look something like:
public ActionResult Index()
{
var model = db.Images.ToList();
return View(model);
}

Can't pass an enumerable model to a controller?

I'm a bit confused because I thought this a very straight-forward thing, it's possibly something simple tripping me up.
I have a view:
#model IEnumerable<CarViewModel>
#using (Html.BeginForm("SummarySaveAll", "VroomVroom", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
#Html.DisplayNameFor(model => model.Driver)
</th>
<th width="1">
#Html.DisplayNameFor(model => model.Colour.Name)
</th>
</tr>
</thead>
<tbody>
#foreach (var element in Model)
{
<tr>
<td width="1">
#Html.DisplayFor(m => element.Driver)
</td>
<td width="1">
#Html.DropDownListFor(m => element.Colour, element.Colours, "Unknown")
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
and the list/enumerable of CarViewModel is supposed to bounce back to the VroomVroom controller, action SummarySaveAll which it does - but the viewmodel on the page doesn't get passed back to it:
[HttpPost]
public ActionResult SummarySaveAll(IEnumerable<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View();
}
I tried to encapsulate the List in another ViewModel and cycle through elements using a for i loop but that wouldn't pass back to the controller either.
Surely it's possible to send a List or IEnumerable of models back to a controller?
My CarVM:
public class CarViewModel
{
[MaxLength(150)]
[Display(AutoGenerateField = true, Name = "Entered By")]
public string Driver { get; set; }
[Display(AutoGenerateField = true)]
public Colour Colour { get; set; }
[Key]
[Display(AutoGenerateField = false)]
public int Id { get; set; }
[Display(AutoGenerateField = false)]
public bool IsDeleted { get; set; } = false;
public IEnumerable<SelectListItem> Colours { get; set; }
public CarViewModel() { }
public CarViewModel(Model CarModel summaryModel, CarPropertyCollection propertyCollection)
{
Driver = summaryModel.Driver;
Id = summaryModel.Id;
IsDeleted = summaryModel.IsDeleted;
Colour = summaryModel.Colour == null ? null :
propertyCollection.Colours.Where(x => x.Id == summaryModel.Colour.Id).FirstOrDefault();
Colours = propertyCollection.Colours.Select(x => new SelectListItem { Value = x.Id.ToString(), Text = x.Name });
}
}
}
Must stress that Colour is a custom class but only has Id and Name properties
Colours doesn't relate to a specific car, it relates to cars in general, so rather than using a collection as your view model, create a wrapper:
class EditCarsViewModel
{
public IEnumerable<SelectListItem> Colours { get; set; }
public IList<CarViewModel> Cars { get; set; }
}
Then your view:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour, Model.Colours, "Unknown")
</td>
}
Any other CarViewModel properties will need their own input as well. HiddenFor can be used if they should be readonly:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
#Html.HiddenFor(m => Model.Cars[i].Id)
#Html.HiddenFor(m => Model.Cars[i].Driver)
<!-- etc. -->
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour.Id, Model.Colours, "Unknown")
</td>
}
And your controller:
[HttpPost]
public ActionResult SummarySaveAll(EditCarViewModel model)
{
// model.Cars should be populated
return View();
}
Note that an indexable collection, such as IList<T> should be used, as the form field names need to include the index to differentiate the items.
Edit by OP
The Colour class consists of a [Key] int Id property and a string Name property. For DropDownList items I had to make sure the Id property was specified on the m => Model.Cars[i].Colour.Id line otherwise that particular prop was coming back as null even though other items were coming through fine.
try
[HttpPost]
public ActionResult SummarySaveAll(IList<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View(summaries);
}
I've also added this model as a param for your view
This how you do it:
First my View which posts back to a controller named Home and an action named ListView:
#model List<MyModel>
#{
ViewData["Title"] = "Using a list as model";
}
<h1>#ViewData["Title"]</h1>
#using (Html.BeginForm("ListView", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
Name
</th>
<th width="1">
Description
</th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td width="1">
#Html.DisplayFor(m => Model[i].Name)
</td>
<td width="1">
#Html.TextBoxFor(m => Model[i].Description)
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
Notice how I used an indexer to render the controls [i]
This is my model:
public class MyModel
{
public string Name { get; set; }
public string Description { get; set; }
}
This is my controller action:
[HttpPost]
public IActionResult ListView(IEnumerable<MyModel> model)
{
return View(model);
}
And this is the result:

Foreign Key property won't set correctly

I have made an earlier post with similar code, and have updated the models to use a helper class instead of a ViewBag. It seemed to work like a charm until the database was updated.
These are my model classes.
[Table("Store")]
public class Store
{
[Key]
public int StoreId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
[Table("Purchase")]
public class Purchase
{
[Key]
public int Id { get; set; }
public int StoreId { get; set; }
[ForeignKey("StoreId")]
public Store Store { get; set; }
}
public class PurchaseDBContext : DbContext
{
public DbSet<Purchase> Purchases { get; set; }
public DbSet<Store> Stores { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
public class PurchaseCreateHelper
{
public Purchase Purchase { get; set; }
public List<Store> Stores { get; set; }
}
}
My relevant part of the View for this question:
<div class="form-group">
#Html.LabelFor(model => model.Purchase.StoreId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Purchase.StoreId, new SelectList(Model.Stores.Select( x => new { StoreId = x.StoreId, DisplayName = x.Name.ToString() + " - " + x.Address.ToString()}), "StoreId", "DisplayName"), new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Purchase.StoreId, "", new { #class = "text-danger" })
</div>
</div>
And the relevant part of my controller:
// GET: Purchases/Create
public ActionResult Create()
{
var purchaseHelper = new PurchaseCreateHelper()
{
Stores = db.Stores.ToList(),
Purchase = new Purchase()
};
return View(purchaseHelper);
}
// POST: Purchases/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Purchase purchase)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchase);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchase);
}
When my code comes to db.Purchases.Add(purchase) the Store property of the purchase object is set to null. The StoreId however seems to be perfectly fine. I don't get why the Store object won't change with the StoreId property as I have annotated it with [ForeignKey("StoreId")]. Where did I mess up the connection between StoreId and Store?
UPDATE #2
// GET: Purchases
public ActionResult Index()
{
return View(db.Purchases.ToList());
}
#model IEnumerable<BookKeeper.Models.Purchase>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Store.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Type)
</th>
<th>
#Html.DisplayNameFor(model => model.Price)
</th>
<th>
#Html.DisplayNameFor(model => model.Date)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => (item.Store.Name))
</td>
<td>
#Html.DisplayFor(modelItem => item.Type)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
#Html.ActionLink("Details", "Details", new { id = item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Id })
</td>
</tr>
}
</table>
UPDATE #3
enter image description here
I assume that you don't see any value for the purchase.Store, that's because the model binder only bind the StoreId (when request is sent from Browser to Web Server) which you specified in
#Html.DropDownListFor(model => model.Purchase.StoreId, new SelectList(Model.Stores.Select( x => new { StoreId = x.StoreId, DisplayName = x.Name.ToString() + " - " + x.Address.ToString()}), "StoreId", "DisplayName"), new { #class = "form-control" })
The populating of purchase.Store belongs to EF. EF will load that Store when you do
dbContext.Purchases.Include(x => x.Store).FirstOrDefault(x => x.ID == 1234);
UPDATE:
If you want the Store object in POST action
// POST: Purchases/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Purchase purchase)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchase);
db.SaveChanges();
purchase = dbContext.Purchases.Include(x => x.Store).FirstOrDefault(x => x.ID == purchase.ID); // Not sure why you need Store information at this step.
return RedirectToAction("Index");
}
return View(purchase);
}

Get data from an Ilist model to a controller [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Ok, please forgive if this is newbie error.. I have been looking here at various solutions but still can't get anywhere.
I need to be able to associate a User (Id) with a Company. I have linked to SelectCompInst and have passed in the user Id.
I have a general bool flag on the companyinstitution model which I set up in the get method. The flag is correctly shown in the view. I can change the fact that its a checkbox if needed.. but I just want to put a button, link that ideally causes a postback in the list in order to allow the operator to select a company to be attached to this user.. something I am sure is done all the time.
I have seen all kinds of references to this sort of problem saying I might have to use an Edit Template? but I never find it explained why? Surely it can't be that complicated can it?
Here's my model:
[Key]
public int Id { get; set; }
public DateTime RecordCreateDate { get; set; }
[ForeignKey("Address")]
public int AddressId { get; set; }
public string Name { get; set; }
[ForeignKey("PrimaryContact")]
public string PrimaryContactId { get; set; }
public virtual Address Address { get; set; }
public virtual ApplicationUser PrimaryContact { get; set; }
[NotMapped]
public bool UserFlag { get; set; } //Bool to define if user is in this activity type when searching
Here's the View:
#model IList<Dune.Models.CompanyInstitution>
#{
ViewBag.Title = "Index";
string staffid = ViewBag.StaffId;
}
<h2>Select Company/Institution for user #ViewBag.UserName</h2>
#using (Html.BeginForm("SelectCompInst","UserAdmin",FormMethod.Post, new {id ="compselect"}))
{
#Html.AntiForgeryToken()
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model[0].Name)
</th>
<th>
#Html.DisplayNameFor(model => model[0].Address)
</th>
<th>
#Html.DisplayNameFor(model => model[0].PrimaryContact)
</th>
<th>
#Html.DisplayNameFor(model => model[0].RecordCreateDate)
</th>
<th></th>
</tr>
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(m => m.ElementAt(i).Name)
</td>
<td>
#{
var AddressString = Model.ElementAt(i).Address.AddressAsString();
}
#Html.DisplayFor(modelItem => AddressString)
</td>
<td>
#{ var NameString = "Not Allocated";
if (Model.ElementAt(i).PrimaryContact != null)
{
NameString = Model.ElementAt(i).PrimaryContact.FullNameNoMidString();
}
}
#Html.DisplayFor(modelItem => NameString)
</td>
<td>
#Html.DisplayFor(modelItem => modelItem.ElementAt(i).RecordCreateDate)
</td>
<td>
#Html.EditorFor(modelItem => modelItem.ElementAt(i).UserFlag)
</td>
<td>
#* Ideally I would like to put a simple 'Select' Link here and also post back the UserId
contained in Viewbag.StaffId.. I figure I can add that to the view model though
if I can get anything at all*#
</td>
</tr>
}
</table>
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Select" />
</div>
}
.. and here's the controller methods:
//
// GET:
public async Task<ActionResult> SelectCompInst(string StaffId)
{
ApplicationUser user = await UserManager.FindByIdAsync(StaffId);
ViewBag.UserName = "Error User not found!";
if (user != null)
{
ViewBag.UserName = user.FullNameString();
ViewBag.StaffId = StaffId;
}
var companysInstitutions = db.CompanysInstitutions.Include(c => c.Address).Include(c => c.PrimaryContact);
// Walk the list and set the flag if this user is already mapped to this institution.
foreach (CompanyInstitution comp in companysInstitutions)
{
if (user.CompanyInstitutionStaffMember.CompanyInstituteId == comp.Id)
{
comp.UserFlag = true;
}
else
{
comp.UserFlag = false;
}
}
IList<CompanyInstitution> companys = await companysInstitutions.ToListAsync();
return View(companys);
}
//
// POST:
[HttpPost, ActionName ("SelectCompInst")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SelectComp(IList<CompanyInstitution> Company )
{
//
// Code removed because it doesnt get this far with any data :)
//
ModelState.AddModelError("", "Something failed.");
return View();
}
I have looked at the post back using fiddler and am getting nothing at all.. although the Post function is being hit. The IList Company is null...
I am sure this boils down to a fundamental misunderstanding on my part but having struggled with it for 2 days now.. have to throw myself on the mercy of the wiser community :)
I would like to put a link against each row in the table and simply pass back an id for the row and the user id.. Thanks.
Further edit after Stephens comment..
The scenario is in the editing of a User. A user can have a CompanyInstituion Id as part of its model. I wish to present a list of companies to the operator (not necessarily the User) to allow them to attach a company id to that user. So in concept its dead simple - present a list of companies to pick from. Press a 'select' link on the company line and return to the controller to sort it out. Hence I need to preserve the UserId in the Viewbag - yes I can create a viewmodel if needed but getting the company id alone would be a start)..
So that's it.. present the list, select one and return it.. I don't really want to use checkboxes anyway.. I kind of 'got there' after trying some things. I originally had the submit button in the loop before I added the checkbox. I did try putting the Company Id on the button but that didn't work either.
If the operator must select a single company that will be associated with the user, then it would make more sense your POST action to take the selected company id and the user id. That should be enough information for you to do the job:
[HttpPost, ActionName ("SelectCompInst")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SelectComp(int companyId, string staffId)
{
...
}
and then in your view you could have multiple forms and a submit button on each row of the table:
#model IList<Dune.Models.CompanyInstitution>
#{
ViewBag.Title = "Index";
string staffid = ViewBag.StaffId;
}
<h2>Select Company/Institution for user #ViewBag.UserName</h2>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model[0].Name)
</th>
<th>
#Html.DisplayNameFor(model => model[0].Address)
</th>
<th>
#Html.DisplayNameFor(model => model[0].PrimaryContact)
</th>
<th>
#Html.DisplayNameFor(model => model[0].RecordCreateDate)
</th>
<th></th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(m => m[i].Name)
</td>
<td>
#{
var AddressString = Model[i].Address.AddressAsString();
}
#Html.DisplayFor(modelItem => AddressString)
</td>
<td>
#{
var NameString = "Not Allocated";
if (Model[i].PrimaryContact != null)
{
NameString = Model[i].PrimaryContact.FullNameNoMidString();
}
}
#Html.DisplayFor(modelItem => NameString)
</td>
<td>
#Html.DisplayFor(modelItem => modelItem[i].RecordCreateDate)
</td>
<td>
#Html.EditorFor(modelItem => modelItem[i].UserFlag)
</td>
<td>
#using (Html.BeginForm("SelectCompInst", "UserAdmin", new { companyId = Model[i].Id, staffId = staffid }, FormMethod.Post, new { #class = "compselect" }))
{
#Html.AntiForgeryToken()
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Select" />
</div>
}
</td>
</tr>
}
</tbody>
</table>
Basically we would have an HTML form on each row of the table which will pass the necessary information to the server:
#using (Html.BeginForm(
actionName: "SelectCompInst",
controllerName: "UserAdmin",
routeValues: new { companyId = Model[i].Id, staffId = staffid },
method: FormMethod.Post,
htmlAttributes: new { #class = "compselect" }))
{
#Html.AntiForgeryToken()
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Select" />
</div>
}

Categories

Resources