I have a working scenario -
My viewmodel contains a list of Employee objects.
List<Employee> Employees;
I populate this list by the following:
Employees = EmployeeService.GetAll().ToList();
This works fine. I can view the employees, update their data, post back and save to db.
However, when I try and sort the list of Employees in the viewmodel before sending to the view by replacing the code above with:
Employees = EmployeeService.GetAll().OrderBy(e=>e.Name).ToList();
The view is populated nicely with the ordered employee details. Unfortunately when I post back this viewmodel to the controller, the viewmodel.Employees is null / empty.
I'd appreciate any help if anyone has any ideas what I might be doing wrong here or why this is happening only when I sort.
Regards
Edit--
public ActionResult Index()
{
EmployeesViewModel _viewModel = new EmployeesViewModel();
return View(_viewModel);
}
[HttpPost]
public ActionResult Index(EmployeesViewModel viewModel)
{
// HERE ** - viewModel.Employees is NULL
EmployeesService.UpdateAllEmployees(viewModel);
return View(viewModel);
}
Edit- Sample Markup --
for (int i = 0; i < Model.Employees.Count(); i++)
{
#Html.HiddenFor(e => e.Employees[i].Id)
#Html.HiddenFor(e => e.Employees[i].Name)
<table>
<tr>
<td style = "width: 125px">
#Model.Employees[i].Name
</td>
<td style = "width: 125px">
#Html.CheckBoxFor(e => e.Employees[i].IsActive)
</td>
</tr>
</table>
}
Edit - Class Details
public class Employee
{
public string Id { get; set; }
public string Name { get; set; }
public string InitialCode { get; set; }
public bool IsActive { get; set; }
public Employee()
{
}
}
public class EmployeesViewModel
{
public List<Employee> Employees { get; set; }
private readonly EmployeesService EmployeesService;
public EmployeesViewModel()
{
Employees = new List<Employee>();
EmployeesService = new EmployeesService();
// Employees = EmployeesService.GetAll().ToList(); //THIS WORKS
Employees = EmployeesService.GetAll().OrderBy(e=>e.Name).ToList();
}
}
EmployeesService simply calls the DB Context and returns all records.
Your HTML form elements need to be rendered in a way that the DefaultModelBinder can correctly parse out the collection. Check out this: https://github.com/danludwig/BeginCollectionItem .
Also read this and this.
Related
I want to store fields and checkboxes in a database table in a form: The connection table contains the following fields:
connection table:
public partial class Connection
{
[Key]
public int ID { get; set; }
public string CommunicationName { get; set; }
public bool IsSelected { get; set; }
}
Register table:
public class RegisterForm
{
#region Ctor
public RegisterForm()
{
}
#endregion Ctor
#region Properties
[Key]
[Required]
public int ID { get; set; }
[Required(ErrorMessage = ("Required"))]
[StringLength(50, ErrorMessage = "This field must be a maximum of 50 characters")]
[TypeConverter("NVarchar(121)")]
[DisplayName("FullName")]
public string FullName { get; set; }
public string Email { get; set; }
public List<Connection> Communications { get; set; }
}
The values of the checkbox fields in the list are displayed using the following method:
questionForm.Communications = db.Connections.ToList<Connection>();
Now how to save the information in the post and save it to the register table. ????????? What changes should be Create to the update, delete operation in the register?
controller for register:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,FullName,PhoneNumber,Email,Communication,")]RegisterForm questionForm)
{
if (ModelState.IsValid)
{
db.Registers.Add(questionForm);
var data = db.SaveChanges();
return View("FormSuccessfullySubmitted");
}
return View(questionForm);
}
You should read about MVC model binding. Normally it could bound it without any problem. But lists are slightly different. You are to provide index of item in list. That is why it is better to use for, instead of foreach.
Check this view and grab it POSTed values to examine. Pay attention, that all list items are displayed using its index in list.
<table class="table">
#using (Html.BeginForm("Bind", "Bind", FormMethod.Post))
{
for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => Model[i].CommunicationName)
</td>
<td>
#Html.CheckBoxFor(modelItem => Model[i].IsSelected)
</td>
</tr>
}
<button type="submit">Submit</button>
}
</table>
I'm trying to build a teacher recommendation web app using sessions for lab, and have gotten to a particular point where I need to view the recommendations that a particular teacher has.
app
When I click on the number of recommendations, it should take me to a view that lists all the recommendations that particular person has, but instead I get an error page saying
'Lab3Models.Models.Person' does not contain a definition for 'Rating'
Here's some of my code, hopefully someone can point me in the right direction.
Recommendation Controller
using Lab3Models.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Lab3Models.Controllers
{
public class RecommendationController : Controller
{
private static IDictionary<string, Person> _people = null;
public ActionResult Add(string id)
{
if (Session["people"] != null)
{
_people = (Dictionary<string, Person>)Session["people"];
}
else
{
_people = new Dictionary<string, Person>();
Session["people"] = _people;
}
return View(_people[id]);
}
[HttpPost]
public ActionResult Create(string personId, Recommendation recommendation)
{
if (personId == null)
{
return HttpNotFound("Error, ID not found");
}
else
{ _people[personId].Recommendations.Add(recommendation);
return RedirectToAction("Index", "Home");
}
}
public ActionResult Show(string id)
{
if (Session["people"] != null)
{
_people = (Dictionary<string, Person>)Session["people"];
}
else
{
_people = new Dictionary<string, Person>();
Session["people"] = _people;
}
return View(_people);
}
}
}
Person & Recommendation Models
public class Person
{
public string Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public ICollection<Recommendation> Recommendations { get; set; }
public Person()
{
Recommendations = new List<Recommendation>();
}
public int NumberOfRecommendations
{
get
{
return Recommendations.Count;
}
}
public class Recommendation
{
public string Id { get; set; }
public int Rating { get; set; }
public string Narrative { get; set; }
public string RecommenderName { get; set; }
public Person ThePerson { get; set; }
}
}
When I put #model IDictionary<string, Lab3Models.Models.Person> in the top of my Show I get the error message 'Person' does not contain a definition for 'Rating' and no extension method 'Rating' accepting a first argument of type 'Person' could be found
If I put #model IDictionary<string, Lab3Models.Models.Recommendation> in the top of my view I get the error message ERROR
If anyone could help me out, it'd be greatly appreciated.
EDIT
#model IDictionary<string, Lab3Models.Models.Recommendation>
#{
ViewBag.Title = "Show";
}
<h2>Show</h2>
<table class="table">
<tr>
<th>
...
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#item.Value.Id
</td>
<td>
#item.Value.Rating
</td>
<td>
#item.Value.Narrative
</td>
<td>
#item.Value.RecommenderName
</td>
<td>
Delete |
</td>
</tr>
}
</table>
EDIT 2
I have #model IDictionary<string, Lab3Models.Models.Recommendation> at the top of my view and have changed the code in my view to look like this:
#foreach (var item in Model)
{
foreach (var rec in item.Recommendations)
{
var rating = rec.Rating;
var narr = rec.Narrative;
...
<tr>
<td>#rating</td>
<td>#narr</td>
<td>#recName</td>
<td>
Delete
</td>
</tr>
}
}
But I'm getting errors in my code specifically on Model in this statement #foreach (var item in Model) and on Value in the delete link. #item.Value.Id When I load the view, I get an error saying
'KeyValuePair' does not contain a definition for 'Recommendations' and no extension method 'Recommendations' accepting a first argument of type 'KeyValuePair'
Did I goof up somewhere logically?
You do want to use #model IDictionary, as that's the type you are using. The issue is that you are getting a type Person out of the dictionary, and attempting to display rating directly from that type. Without seeing your front-end code I can't pinpoint exactly how the issue is presenting, but can tell you what your issue is. Essentially, you are attempting to get the Rating property from the person object, but the Rating property is part of the Person object's Recommendation Collection.
I'm assuming here that you are iterating through each Person in the dictionary to build out the display. You also need to iterate through each Recommendation for each person if you want to access the Rating.
roughly
foreach(var person in #model) {
//person specific display things
foreach(var recommendation in person.Recommendations) {
var rating = recommendation.Rating;
// display rating things
}
}
So I am currently studying and analyzing the use of ViewModels.
In my Application (a so called "Restaurant") I want the ability for my "users" to create a menu.
When they want to create a menu: They can choose the name + the amount of persons that can join the menu. BUT also, they can add an amount of dishes that are already in the restaurant. This will be in the style of checkboxes and an 'Create'-Button at the end.
This means I had to use a ViewModel. I am currently trying to give the possibility to add a list of dishes to a menu for the creation. But I'm stuck at the for loop, used to loop through the dishes. Or better, I'm stuck at the whole concept:
What is the best way to display all the already created dishes to the CreateMenu View? Is it still possible to loop through a ViewBag if I will add them in a ViewBag?
Lets say I successfully tried to do what I wanted to do. How would I create a new Menu based (or extracted?) from the ViewModel?
In my Code, please note that the Menu - Model cannot be changed really because I already use a list of Dishes from it (In another view, where I display all the menu's and their dishes).
also ignore the possibility of wrong names or spelling mistakes in data, since I translated everything from Flemish
Models
public class Menu
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(0,10)]
public int AmountPersons { get; set; }
[Range(0,double.MaxValue)]
public double Price { get; set; }
public virtual List<Dish> Dishes { get; set; }
}
public class Dish
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public enum Types { VOORGERECHT, HOOFDGERECHT, DRANK, DESSERT}
public Types Type { get; set; }
public double Price { get; set; }
public virtual List<Menu> Menus { get; set; }
public virtual List<Table> Tables { get; set; }
//Checked used for the 'checkbox' in the CreateMenu-View
[NotMapped]
public bool Checked { get; set; }
}
public class MenuViewModel
{
public Menu Menu { get; set; }
public List<Dish> AddedDishes { get; set; }
}
Controller
public ActionResult CreateMenu( )
{
MenuViewModel gm = new MenuViewModel();
// Assign ALL already created dishes to the list that the user can choose.
// AddedDishes is wrong? ViewBag preferred?
gm.AddedDishes = db.Dishes.ToList();
return View(gm);
}
// Add the Menu to all the Menu's in the Database.
[HttpPost]
public ActionResult MenuAanmaken(MenuModel gm)
{
// code to save the menu with all the added dishes to the database
// Conflict!? Cannot convert the MenuViewModel to the Menu-model How do we need to extract the Menu and the AddedDishes list
// to a menu and save that one to the database?
db.Menus.Add(gm);
return View(gm);
}
View
#using VBExamen.Models
#model MenuViewModel
....
#Html.LabelFor(m => m.Menu.Name)
#Html.EditorFor(m => m.Menu.Name)
#Html.LabelFor(m => m.Menu.AmountPersons)
#Html.EditorFor(m => m.Menu.AmountPersons)
#for(int i = 0; i < Model.AddedDishes.Count; i++)
{
<tr>
<td>
#Html.DisplayFor( .Name)
#Html.HiddenFor(item => .Id)
#Html.CheckBoxFor(item => .Checked)
</td>
</tr>
}
E D I T E D _ U P D A T E (SEE BELOW)
Okay So I think I'm close now,
I edited my classes as the following:
public class MenuViewModel<T>
{
public Menu Menu { get; set; }
public List<T> DishList { get; set; }
public MenuViewModel()
{
this.Lijst = new List<T>();
}
}
Controller
public ActionResult CreateMenu(MenuViewModel<Dish> model )
{
model.DishList = db.Gerechten.ToList();
return View(model);
}
[HttpPost]
public ActionResult CreateMenu(MenuViewModel<Dish> model,List<Gerecht> SelectedList)
{
Menu t = new Menu();
t.Naam = gm.Menu.Naam;
t.AmountPersons = gm.Menu.AmountPersons;
t.Dishes = SelectedList;
db.Menus.Add(t);
return View("Menus", model);
}
View function creating list
#for (int i = 0; i < Model.DishList.Count(); i++)
{
<tr>
<td>
#Html.Label(Model.DishList[i].Naam)
<input type="hidden" name=#String.Format("DishList[{0}].Id", i) value=#Model.DishList.ElementAt(i).Id />
<input type="hidden" name=#String.Format("DishList[{0}].Name", i) value=#Model.DishList.ElementAt(i).Name />
<input type="checkbox" name=#String.Format("DishList[{0}].value", i) />
<input type="hidden" name=#String.Format("DishList[{0}].value", i) value="false" />
</td>
<br />
</tr>
}
I did this after watching about 10 tutorials about ViewModels, is my next approach better than the first one?
I think so because i get the following on my screen:
I was thinking what the next approach would be. I was thinking about comparing the 2 lists (1 of the viewmodel, 1 passed) and see the checkbox statuses?
UPDATE
After Stephen Muecke's answer I re-edited my code but found a problem that I can't seem to understand.
The answer says I should be in the position of a 1-to-many table in the form as a class:
// You have not indicated the 1-many table the dishes are saved to so adjust as required
MenuDish dish = new MenuDish()
{
MenuId = menu.ID,
DishId = dish
};
db.MenuDishes.Add(dish);
However, what we've learned at school was, that if you create lists in the data-models of the entities, linked tables will be automatically generated in the Database. And that is exactly what my DB has done (without the creation of the MenuDish class):
MenuGerechts stands for MenuDish.
This is the automatically created table done by the entity framework.
That brings me to the following questions. I have re-edited the controller to the following:
[HttpPost]
public ActionResult MenuAanmaken(MenuVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
IEnumerable<int> selectedDishes = model.Dishes.Where(x => x.IsSelected).Select(x => x.ID);
Menu menu = new Menu()
{
Naam = model.Name,
AantalPersonen = model.AmountPersons
};
foreach (int id in selectedDishes)
{
Dish g = db.Dishes.Find(id);
menu.Dishes.Add(g);
};
db.Menus.Add(menu);
db.SaveChanges();
return RedirectToAction("Menus", "Menu");
}
I get the Object reference not set to an instance of an object error and I'm understanding why ofcourse.
I have done the changes since the Data-Model Menu, already has a List of Dishes. But assuming the answer of S. Muecke, this isn't the correct way to solve this ViewModel since he proposes the use of a New Class (that is created to support the one-to-many relationship)?
This brings me to the conclusion of the following questions:
Why is it impossible or not-recommended to directly add the selected dishes to the menu instance?
Is it always needed to create the in between table 'MenuDish' in a Data-model?
Will the following code still work (showing the menu's and their dishes) after creating new Menu's?:
Controller:
public ActionResult Menus()
{
List<Menu> menus = db.Menus.ToList();
return View(menus);
}
View:
#model IEnumerable<VBExamen.Models.Menu>
#{
ViewBag.Title = "Menus";
}
<h2>Menus</h2>
<p>
#Html.ActionLink("Create New Menu", "CreateMenu")
</p>
#foreach (var item in Model)
{
<table>
<ul>
<p>#Html.DisplayFor(modelItem => item.Name)</p>
#foreach (var g in item.Dishes)
{
<li>
#Html.DisplayFor(modelItem => g.Name)
</li>
}
</ul>
</table>
}
Which outputs the following:
What would be good motivations to do this?
UPDATE 2
So I have included the following in my project:
** I have used the Table()- annotation to make it use the one that's already created**
**Model: **
[Table("MenuGerechts")]
public class MenuGerechts
{
[Key]
[ForeignKey("Menu")]
public virtual int? MenuId { get; set; }
public virtual Menu Menu { get; set; }
[ForeignKey("Dish")]
public virtual int? DishId { get; set; }
public virtual Dish Dish { get; set; }
}
I have then actually created new menus successfully! But when I go to the overview menu page (from the pic above), it only shows the Name of the menu, and not the list of meals that it includes.
The Database however didn't allow my MenuDish link table to be used for my newly created class (it created a new one, and renamed the old one with the 'old' menus with a '1' behind it:
Hence why I was asking my previous questions. Does this mean my whole approach to this exercise was wrong?
New Question:
My menuCreate ViewModel only works if i Select 1 dish? Why is this so? I get the following error The role 'MenuGerechts_Menu_Source' of the relationship 'VBExamen.Models.MenuGerechts_Menu' has multiplicity 1 or 0..1.
Firstly a view model should not contain properties which are data models. It should contains only properties which you display/edit in the view, and I recommend you read What is ViewModel in MVC?.
Based in the image of the form you have shown, your view models needs to be (display and validation attributes omitted for simplicity)
public class MenuVM
{
public int? ID { get; set; } // included so this can be used for editing as well as creating
public string Name { get; set; }
public int AmountPersons { get; set; }
public List<DishVM> Dishes { get; set; }
}
public class DishVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
and the controller GET method
public ActionResult CreateMenu( )
{
// Get all the dishes from the database
var dishes = db.Dishes; // modify to suit
// Initialize the view model
var model = new MenuVM()
{
Dishes = dishes.Select(x => new DishVM()
{
ID = x.Id,
Name = x.Name
}).ToList()
};
return View(model);
}
Then in the view (LabelFor() and ValidationFor() methods omitted for simplicity)
#model MenuVM
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.Name)
#Html.TextBoxFor(m => m.AmountPersons )
for(int i = 0; i < Model.Dishes.Count; i++)
{
<div>
#Html.HiddenFor(m => m.Dishes[i].ID)
#Html.HiddenFor(m => m.Dishes[i].Name)
#Html.CheckBoxFor(m => m.Dishes[i].IsSelected)
#Html.LabelFor(m => m.Dishes[i].IsSelected, Model.Dishes[i].Name)
</div>
}
<input type="submit" value="Create" />
}
And finally the POST method will be
public ActionResult CreateMenu(MenuVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Initialize and save the Menu
Menu menu = new Menu()
{
Name = model.Name,
AmountPersons = model.AmountPersons
};
db.Menus.Add(menu);
db.SaveChanges(); // you now have the ID of the new menu
// Save the dishes associated with the menu
IEnumerable<int> selectedDishes = model.Dishes.Where(x => x.IsSelected).Select(x => x.ID);
foreach(int id in selectedDishes)
{
// You have not indicated the 1-many table the dishes are saved to so adjust as required
MenuDish dish = new MenuDish()
{
MenuId = menu.ID,
DishId = dish
};
db.MenuDishes.Add(dish);
}
db.SaveChanges(); // save the selected dishes
return RedirectToAction(...); // redirect somewhere
}
Side note: Remove the [NotMapped] public bool Checked { get; set; } property from your data model.
This is only the answer to your first question... I gotta get back to work :)
I strongly advise you to use Entity Framework for storing this information, as creating the data context, Initializer(Entity Framework Requirements) and View Model will allow you to scaffold everything in your project including controllers and views. This means you take the information from the ViewModel class rather than from the view bag.
Scaffolding means that Visual Studio will create all your code to CRUD(Create, Read, Update, Delete) Controllers and Views to allow this to happen. Freeing you from either 45 mins of furious typing or hours of banging your head against a wall.
So lets do this, First we create our own context class inheriting from DbContext (A part of Entity Framework):
public class MenuContext : DbContext {
public MenuContext() : base("MenuContext") {
}
The base referenced here specifies the name in your web.config file of your connection string which we will set up momentarily. Alternatively you can specify your connection string in place of the name.
public DbSet<MenuViewModel> Menus { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
The Initializer class we will set up next populates the database if the database does not already exist at the connection string location.
class MenuInitializer : CreateDatabaseIfNotExists<MenuContext> {
protected override void Seed(MenuContext context) {
// This sets up your database to populate with whatever you type into this method.
}
}
Now you are able to go to your solution explorer, right click on your controllers folder and click add - Controller. Then specify that you want MVC 5 Controller with views, using Entity Framework. Click - Add.
A dialog will show, specify your view model, the context class we set up, make sure "Generate Views" is selected, name your controller and BAM! Build your project and view your auto created everything!
I'm still working on my asp.net app. I have a page where orders table is displayed and i would like to include "details"column, so someone can choose order and then look up it's details.
Here is my View code:
<table id="example" class="table table-striped table-bordered" cellspacing="0" width="100%">
<thead>
<tr>
<th>ID</th>
<th>Seat</th>
<th>Movie</th>
<th>Date</th>
<th>Details</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.OrderList.results)
{
<tr>
<td>#item.objectId</td>
<td>#item.Seat</td>
<td>LOTR</td>
<td>#item.createdAt</td>
<td>Details</td>
</tr>
}
</tbody>
</table>
Ofcourse Url.Action is just for testing. Here is my controller method:
public ActionResult Details(OrderModel model)
{
return View(model);
}
Results is a List of OrderModel objects. I'd like to pass one OrderModel object that corresponds with chosen table row. The whole point is to display OrderModel object contents on Details page. Can someone explain me how to do that?
EDIT: My model:
OrderModel
public class OrderModel
{
/*class representing Order data*/
public string Seat { get; set; }
public string objectId { get; set; }
public DateTime? createdAt { get; set; }
public DateTime? updatedAt { get; set; }
}
My root for ordermodel(needed for json deserialization to list of objects)
public class OrderRootModel
{
public List<OrderModel> results { get; set; }
}
My baseviewmodel orderlist line(viewmodel is shared along all sites - i'm using shared layout):
public OrderRootModel OrderList { get; set; }
EDIT2:
Alright so after looking at my code i modified it, so Details page recieves BaseViewModel instead of ordermodel(i'm using shared layout).
BaseViewModel:
public class BaseViewModel
{
public OrderModel Order { get; set; }
public OrderRootModel OrderList { get; set; }
}
OrdersController:
public ActionResult Details(OrderModel order)
{
BaseViewModel model = new BaseViewModel();
model.Order = order;
return View(model);
}
Alright guys. I think i didn't get enough sleep so my thought process was a little bit off. As #user1672994 suggested i can just pass order id to details view(if i remember right passing whole object between view and controller is not recommended).
So if anyone is interested here is a solution:
View:
#foreach (var item in Model.OrderList.results)
{
<tr>
<td>#item.objectId</td>
<td>#item.Seat</td>
<td>LOTR</td>
<td>#item.createdAt</td>
<td>Details</td>
</tr>
}
Controller:
public ActionResult Details(string id)
{
ApiModel data = new ApiModel();
BaseViewModel model = new BaseViewModel();
model.Order = data.GetOrderData(id);
return View(model);
}
Model:
public OrderModel GetOrderData(string id)
{
OrderModel model = new OrderModel();
string url = "https://api.parse.com/1/classes/Orders" + "/" + id;
model = JsonConvert.DeserializeObject<OrderModel>(getParseIdData(url));
return model;
}
It works perfect. Thank you guys.
I am having alot of difficult getting my viewmodel to work correctly. As a bit of context i am using a viewmodel to use two models from another project. These models contain the User information and thier chosen Device in a localdb. However i cannot currently use a view to display the data from both of those models on one view so i created a viewmodel.
However I am current recieving:
Error: 'System.Collections.Generic.IEnumerable' does not contain a definition for 'UserID' and no extension method 'UserID' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?)
This error is occurring for all of the model objects. If i can get around it it will be the first step to a functioning view model. Any help would be greatly appreciated.
User.cs - Model (in project: FaceToFace)
namespace FaceToFace.Model
{
public class User
{
public int UserID { get; set; }
public string CodeName { get; set; }
public bool UseBriefInstructions { get; set; }
public ICollection<RegimeItem> RegimeItems { get; set; }
public Device Device { get; set; }
public virtual ICollection<Grading> UserGradings { get; set; }
public User()
{
this.RegimeItems = new List<RegimeItem>();
Device = new Device();
}
}
public class RegimeItem
{
public int RegimeItemID { get; set; }
public Exercise RegimeExercise { get; set; }
}
}
Device.cs - Model (in project: FaceToFace)
namespace FaceToFace.Model
{
public class Device
{
public int DeviceID { get; set; }
public string Name { get; set; }
}
}
UserDeviceViewModel.cs (in project: FaceToFaceWebsite)
namespace FaceToFaceWebsite.Models
{
public class UserDeviceViewModel
{
public UserDeviceViewModel()
{
User = new User();
Devices = new List<SelectListItem>();
}
public User User { get; set; }
public IList<SelectListItem> Devices { get; set; }
}
}
PatientController.cs - Only a segment of the entire page to avoid spam (Project: FaceToFaceWebsite)
namespace FaceToFaceWebsite.Controllers
{
public class PatientController : Controller
{
private F2FData db = new F2FData();
public ActionResult Index()
{
var viewModel = new List<FaceToFaceWebsite.Models.UserDeviceViewModel>();
return View(viewModel);
}
}
}
Views/Patient/Index.cshtml (facetofacewebsite)
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
#*#model FaceToFaceWebsite.Models.UserDeviceViewModel*#
#*#model IEnumerable<FaceToFace.Model.User>*#
<h2>Your Patients</h2>
#*Showing #Model.Count() users*#
<p>#Html.ActionLink("Add New User", "Create")</p>
<table>
<tr>
<th>#Html.DisplayNameFor(model => model.UserID)</th>
<th>#Html.DisplayNameFor(model => model.CodeName)</th>
<th>#*#Html.DisplayNameFor(model => model.Device.Name)*#Device</th>
<th>#Html.DisplayNameFor(model => model.DeviceID)</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.UserID)</td>
<td>#Html.DisplayFor(modelItem => item.CodeName)</td>
<td>#Html.DisplayFor(modelItem => item.Name)</td>
<td>#Html.DisplayFor(modelItem => item.DeviceID)</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.UserID }) |
#Html.ActionLink("Details", "Details", new { id = item.UserID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.UserID })
</td>
</tr>
}
</table>
So what i REALLY need to know is that by using model properties from another project, what do i have to do differently. What am i currently doing wrong? what should i do so that the USER information and the DEVICE information can be show.
UPDATE
Thanks to Stephen Muecke, the solution to the issue of the index view not displaying the user db data was solved by changing the Action result in the index controller to:
public ActionResult Index()
{
var viewModel = db.Users.Select(u => new UserDeviceViewModel() { User = u, Device = u.Device }).ToList();
return View(viewModel);
}
UserDeviceViewModel contains a property named User not UserID (which is a property of User. Your loop needs to be
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(m => item.User.UserID)</td>
<td>#Html.DisplayFor(m => item.User.CodeName)</td>
Note you table headers wont work in this case.
Note also you are not really using a true 'view model'. A view model contains only those properties which you need for display/edit in a view (not just for dumping other models). Based on the view code you have shown it should be something like
public class UserDeviceViewModel
{
public int UserID { get; set; }
public string CodeName { get; set; }
public int DeviceID { get; set; }
public IList<SelectListItem> Devices { get; set; }
}
Although you view contains a reference to property Name (not sure what this is - perhaps DeviceName?) and your view does not use Devices (have you omitted some of the view?)
Remove the Ienumerable!
#model FaceToFaceWebsite.Models.UserDeviceViewModel
Look in your controller:
public ActionResult Index()
{
var viewModel = new
FaceToFaceWebsite.Models.UserDeviceViewModel();
return View(viewModel);
}
You are passing List (IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>) , while your view code expected to be FaceToFaceWebsite.Models.UserDeviceViewModel
Well you could pass the Correct type of ViewModel to your View:
In your View you have:
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
And in Controller you have:
var viewModel = new FaceToFaceWebsite.Models.UserDeviceViewModel();
return View(viewModel);
Try passing a List of your ViewModel:
var viewModel = new List<FaceToFaceWebsite.Models.UserDeviceViewModel>();
return View(viewModel);
OR:
In your View change this:
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
To:
#model FaceToFaceWebsite.Models.UserDeviceViewModel
WHY are you getting that ERROR Message?
Because your ViewModel doesn't have UserId, CodeName etc.
BUT your User Class has UserId and CodeName
So In ViewModel you will access like this:
ViewModel.User.UserId and ViewModel.User.CodeName:
Like This:
<th>#Html.DisplayNameFor(model => model.User.UserID)</th>
<th>#Html.DisplayNameFor(model => model.User.CodeName)</th>
As per given code snippet, your View mapping and data model not sync up.
can you just follow below steps
clear all views.
first display only user level info
Verify user level info are you able to? next proceed further device level
put device level loop for your devices collection (make sure your Device collection model m not sure about your "SelectedListItem"