ViewModel not working. Display from two tables - c#

I'am trying to view information from two tables in a view with the view model, but it does not work.
This gives two tables with information.
public class HeatImage
{
[Key]
public int ImageId { get; set; }
public string name { get; set; }
}
public class HeatingArea
{
[Key]
public int ID { get; set; }
public string Area { get; set; }
}
My viewmodel
public class HeatingAreaViewModel
{
public IEnumerable<HeatImage> heatingImage { get; set; }
public IEnumerable<HeatingArea> heatingArea { get; set; }
}
My controller
public ActionResult Index()
{
HeatingAreaViewModel mymodel = new HeatingAreaViewModel();
mymodel.heatingArea = db.HeatingAreas.ToList();
mymodel.heatingImage = db.HeatImages.ToList();
return View(mymodel);
}
And my view
#model IEnumerable<Project.Models.HeatingAreaViewModel>
#foreach (var item in Model)
{
#item.heatingArea
}
Error
The model item passed into the dictionary is of type
'Project.Models.HeatingAreaViewModel', but this dictionary requires a
model item of type
'System.Collections.Generic.IEnumerable`1[Project.Models.HeatingAreaViewModel]'.

The error message tells it: the View expects IEnumerable of HeatingAreaViewModel, while your controller action sends to the view only HeatingAreaViewModel.
So either correct the controller to send a list of HeatingAreaViewModel, or correct the view to expect only a single instance of HeatingAreaViewModel.

You are sending through appels and expecting oranges, that is why you getting the error.
In your controller you are sending through a single (one) instance of HeatingAreaViewModel. In your view you are making provision to except a list of HeatingAreaViewModel instances (more than 1).
Reading through your replies you want to use both HeatImage and HeatingArea in each loop iteration. You will need to change your view model to accommodate this. For example, create a view model that can accommodate both:
public class HeatViewModel
{
public HeatImage HeatImage { get; set; }
public HeatingArea HeatingArea { get; set; }
}
You will pass this HeatViewModel as a list to your view.
public ActionResult Index()
{
// This is just dummy code
HeatingViewModel model1 = new HeatingAreaViewModel();
// Populate the properties
model1.HeatImage = new HeatImage();
// Populate the properties
model1.HeatingArea = new HeatingArea();
HeatingViewModel model2 = new HeatingAreaViewModel();
// Populate the properties
model2.HeatImage = new HeatImage();
// Populate the properties
model2.HeatingArea = new HeatingArea();
// Now pass this list to the view
List<HeatingViewModel> models = new List<HeatingViewModel>();
return View(models);
}
In your view your code would look something like this:
#model List<Project.Models.HeatingViewModel>
#foreach (var item in Model)
{
<p>item.HeatImage.Name</p>
<p>item.HeatingArea.ID (I have used ID because I don't know what your Area looks like)</p>
}
This way you have both objects in a single loop. You will just have to go and figure out how you are going to populate them, this is where the bulk of the work will be done.
I also noticed that you you start your properties in the lower case, best practices start them with caps. For example:
public HeatImage heatImage { get; set; }
...should be...
public HeatImage HeatImage { get; set; }
I hope this helps :)

Your view is expecting an IEnumerable of HeatingAreaViewModel and it seems as though your only passing a single instance.
Change this
#model IEnumerable<Project.Models.HeatingAreaViewModel>
to this
#model Project.Models.HeatingAreaViewModel
Then you'll be able to loop through the heatingArea and heatingImage properties like this
#foreach (var item in model.heatingArea)
{
//what ever you need to do here
}

If your problem is, how to iterate in one loop through the two lists of HeatingImage and HeatingArea, in your view, you have to redo your viewModel:
public class HeatingAreaViewModel
{
public HeatImage heatingImage { get; set; }
public HeatingArea heatingArea { get; set; }
}
and then the controller action:
public ActionResult Index()
{
heatingAreas = db.HeatingAreas.ToList();
heatingImages = db.HeatImages.ToList();
List<HeatingAreaViewModel> myModel = heatingAreas.Zip(heatingImages, (a, b) => new HeatingAreaViewModel {HeatingArea = a, HeatImage = b})
return View(mymodel);
}
Then the View will work, as it is.
However, I strongly advice against doing it this way. You would have to ensure, that corresponding elements in the two lists are in reality corresponding to each other. Which is very prone to errors. If there is an underlying logic that ties these two lists together (some relationship on database tables), I would use that one to join the lists/tables together, instead.

Simply your Model in the view doesn't matches what you sent from controller action, so I think you need to change your view to be like this:
#model HeatingAreaViewModel
#foreach (var item in Model.heatingImage)
{
#item.heatingArea
}

Related

MVC Two controllers One View

I am trying to have a details view in an existing controller that joins two controllers and passes into the view, but everything i have tried so far has not worked. I have 4 data models and what i would like is to use 2 of them Company and Staff. So when i select details on a specific Company it will return all Staff associated to that Company in the same view.
HRDataModel class
public partial class HRDataModel : DbContext
{
public HRDataModel()
: base("name=HRDataModel")
{
}
public virtual DbSet<Company> Companies{ get; set; }
public virtual DbSet<Attribs> Attribs{ get; set; }
public virtual DbSet<Staff> Staffs { get; set; }
....
Company Data Model
[Table("Company")]
public partial class Company
{
public Company()
{
Staffs = new HashSet<Staff>();
}
public virtual ICollection<Staff> Staffs { get; set; }
public int companyId{ get; set; }
[Required]
[StringLength(10)]
public string companyName{ get; set; }
.....
Staff Data Model
public partial class Staff
{
public Staff()
{
Skills = new HashSet<Skill>();
}
public virtual Company Company{ get; set; }
public virtual ICollection<Skill> Skills { get; set; }
public int staffId { get; set; }
.........
And i am trying to get my Details method in CompanyController to show details of all active Companies in the db and also all Staff attached to that Company
[Route("Company/Staff/1}")]
public ActionResult Details(int id)
{
Company co = db.Companies.Find(id);
...How to implement????
return View(bu);
}
If anyone can point me in the right direction that would be great. I have tried and tried but cannot get anything to work?
Since Company includes Staff you can use the include method to include related entities.
var company = db.Companies.Include(c => c.Staffs).FirstOrDefault(x => x.id == companyId);
return View(company);
And in your view:
#foreach(var staff in Model.Staffs) { ... }
You need to pass a data structure which has the company info and staff details to your view. You may pass your existing Comapny entity class to do this. But the problem is, It makes your razor view tightly coupled to your Entity which was generated by the ORM. What if you switch your Data access layer to something else tomorrow. So this solution is not great.
So you should use a view model ( A simple POCO class) which has properties which you need to render in the view. Then read your entity from db in your action method, map it to a vie wmodel instance and send it.
Create a view model like this.
public class CompanyInfo
{
public int Id {set;get;}
public string Name {set;get;}
public List<StaffItem> Staffs {set;get;}
public CompanyInfo()
{
this.Staffs = new List<StaffItem>();
}
}
public class StaffItem
{
public int Id {set;get;}
public string Name {set;get;}
}
In your action method read the Company entity and map it to the view model
public ActionResult Details(int id)
{
var vm = new ComapnyInfo();
var company = db.Companies
.Include(r => c.Staffs)
.FirstOrDefault(x => x.companyId==id);
if(co!=null)
{
//Map the property values now
vm.Name = co.companyName;
vm.Id = co.companyId;
if(co.Staffs!=null)
{
vm.Staffs = co.Staffs.Select(f=> new StaffItem {
Id=f.staffId,
Name = f.staffName}).ToList();
}
}
return View(vm);
}
Now your view should be bound to the CompanyInfo view model
#model YourNamespace.CompanyInfo
<h2>#Model.Name</h2>
<h3>Staffs</h3>
#foreach(var staff in ModelStaffs)
{
<p>#staff.Name</p>
}
If you do not like the manual mapping, You may consider using a mapping libarary like Automapper.
Hi #stackface you dont pass two controllers to get both views for that what you do is create one View Model which is essentially a container for multiple models and pass that into the view from the controller.
E.g. Model 1, Model2, ModelN all are needed so you have a class and in that class it has properties consisting of Model1, Model2 and Model3 so that way you pass in your class which has all the needed models.
E.g.
public class Company{...}
public class Staff{...}
public class ViewModel{
public Company Company {get;set;}
public List<Staff> Staff{get;set;}
}
controller:
{
var viewModel = new ViewModel();
viewModel.Company = db.Companies.FirstOrDefault(x => x.id == companyId);
viewModel.Staff = db.Staff.Where(x => x.CompanyId == campanyId).ToList() //or whatever your properties are called.
Return View(viewModel);
}
Update your view to take type ViewModel.
You can also compose a view by calling controller actions that return partial views.
The advantage is that you can reuse the partial views and their actions, e.g. to show the company details with the same layout on different pages. This increases consistency and maintainability.
The drawback is that you loose flexibility: if a certain page requires a different layout, you should create a new view. Performance might also be lower because you hit the backend with many small operations instead of a single big one.
The combined viewmodel to show both company and staff details only needs to know how to access the required data:
public class CompanyAndStaffDetailsViewModel {
public long CompanyId { get; set; }
public long StaffId { get; set; }
}
The following action renders the combined view:
public ActionResult Details(long companyId, long staffId) {
var viewModel = new CompanyAndStaffDetailsViewModel {
CompanyId = companyId,
StaffId = staffId
};
return View("Details", viewModel);
}
The "Details" View composes the usecase by calling actions to render partial views:
#model CompanyAndStaffDetailsViewModel
#Html.Action("CompanyInfoPartial", "Company", new { companyId = Model.CompanyId })
#Html.Action("StaffInfoPartial", "Staff", new { staffId = Model.StaffId })
The "Company" controller provides a reusable action to render company details:
public ActionResult CompanyInfoPartial(long companyId) {
Company co = db.Companies.Find(companyId);
var model = Mapper.Map<CompanyViewModel>(co); // map persistable entity to ViewModel
return PartialView("_CompanyInfoPartial", model);
}
Then the parital View _CompanyInfoParital.cshtml only has to deal with the Company Info stored in the CompanyViewModel and knows nothing about Staff:
#model CompanyViewModel
#Html.DisplayFor(m => m.CompanyName)
// etc ...
The idea for the StaffInfoPartial action is the same as for CompanyInfoPartial.

Return 2 lists from one model to a view in MVC

I am new to MVC, and I am struggling with viewmodels. I would like to return 2 lists from the same model into one view. I will need 2 foreach loops to show records of a different "status" on one view. Because both lists are coming from one model, is it necessary to create a viewmodel?
I have tried the following, but my view is not finding the item type for each list.
public class PipelineViewModel
{
public int LeadID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Status{ get; set; }
public string LoanAgent{ get; set; }
public List<WebLead> PipeNewLeads { get; set; }
public List<WebLead> PipeDispLeads { get; set; }
}
Note the List is domain model for the table I am pulling the lists from. Is that correct?
Next in my controller:
public ActionResult Index(string LoanAgent)
{
var viewModel = new PipelineViewModel
{
PipeNewLeads = db.WebLeads
.Where(l => l.LoanAgent.Equals(LoanAgent) && l.Status.Equals("New")).ToList(),
PipeDispLeads = db.WebLeads
.Where(l => l.LoanAgent.Equals(LoanAgent) && l.Status.Equals("Disp")).ToList(),
};
return View(viewModel);
I know that the controller is wrong. I need to be referencing the viewmodel somehow, but I have tried a few ways and keep getting errors.
In the view, I used
#model LoanModule.ViewModels.PipelineViewModel
and I tried to call each list like this, but it didn't work.
#foreach (var item in Model.PipeNewLead)
#foreach (var item in Model.DispNewLead)
I think I am almost there, but I am not sure what I am doing wrong in the controller. I would appreciate any help or references!
Consdering you have:
public ActionResult Index(string LoanAgent)
{
var viewModel = new PipelineViewModel
{
PipeNewLeads = ....
PipeDispLeads = ....
};
return View(viewModel);
}
Your view foreachs should be:
#foreach (var item in Model.PipeNewLeads)
#foreach (var item in Model.PipeDispLeads)
Note the spelling ;-)
This works perfectly fine for me:
public ActionResult Contact()
{
var viewModel = new PipelineViewModel
{
PipeNewLeads = new List<WebLead>(),
PipeDispLeads = new List<WebLead>(),
};
return View(viewModel);
}
And view:
#model WebApplication1.Controllers.PipelineViewModel
#foreach (var item in Model.PipeDispLeads)
{
<p>Disp</p>
}
#foreach (var item in Model.PipeNewLeads)
{
<p>New</p>
}
Your issue is somewhere else.
I would:
Look at where you are referencing IEnumerable<PipelineViewModel> and find out why. This is most likely in a completely different Razor view, therefore making that error unrelated.
Check what your entity framework is returning. The EF error you are getting is probably because either:
Database not available
EF Model is different to database
Login issues
Network issues
Obviously your EF error could be something different, but those are some suggestions
Came across this issue...I think it's just a typo.
You have public List<WebLead> PipeNewLeads { get; set; } in model but #foreach (var item in Model.PipeNewLead) in the view. Note the plural, so of course you saw the exceptions your got (not found/does not contain).
You can test to see if you have data from your model in this way:
#if(Model.PipeNewLeads != null && Model.PipeNewLeads.Count > 0)
{
foreach (var item in Model.PipeNewLeads)
{
}
else
{
<p>No PipeNewLeads</p>
}
}
But you really should be creating Unit Tests where you'd be able to determine which models are actually in your view.

Asp net MVC 5 and listbox with linq

After several days of reading on how to fill a listbox with linq i am completely lost on what I have to do to make this work. I know I have to have a viewmodel or something like that but since i cannot reference 2 models in the same view I don t know what to do.
this is the model i have
public class ABC
{
public int a { get; set; }
public int b { get; set; }
public int c { get; set; }
public string d { get; set; }
public string Type { get; set; }
public int ID { get; set; }
public class ABCDBContext : DbContext
{
public DbSet<ABC> ABCs { get; set; }
}
}
public class ABCModel
{
public string type{ set; get; }
public List<ABC> ABCs { set; get; }
}
The controller i know i am missing a lot of things but I donĀ“t know how to fill the list with that linq query nor how to call this controller from the view without using beginform (I will have more than 1 listbox in this form )
public ActionResult GetStuff()
{
var Types = from m in db.ABCs
select m.Type.Distinct();
return View(Types);
}
And then on my view i am required to have
#model IEnumerable so i can show on that page everything that the table abc has.
So, can I create a viewModel that fills the lsitbox (I will need to fill at least 6 with the parameters that abc has so the user can search for abcs with those values, but i suppose that if i can understand how to do 1 i can do it for 6) with what I want and that allows me to show the entries that abc has?
and this is the view ( i know a lot of things are wrong with it but its just to give an example)
#using (Html.BeginForm("getSelectedValues", "ABC", FormMethod.Get))
{
Type:#Html.DropDownList()
a:#Html.DropDownList()
b:#Html.DropDownList()
//and so on
<input type="submit" value="Filter" />
}
Pardon for my ignorance for any aspect i missed but i am new to asp net mvc 5, also if anyone could just guide me or send me a guide on what I have to do i would greatly appreciate it.
What I would recommend is you create a ViewModel named MYPAGENAMEViewModel.cs, then in that ViewModel you have all the 'model' / data that you need for the view.cshtml file.... i.e. something like the following:
public class MYPAGENAMEViewModel
{
public List<Types> MyTypes { get; set; }
public List<OtherTypes> MyOtherTypes { get; set; }
}
then in your action.
public ActionResult GetStuff()
{
MYPAGENAMEViewModel myViewModel = new MYPAGENAMEViewModel();
myViewModel.MyTypes = from m in db.ABCs
select m.Type.Distinct();
myViewModel.MyOtherTypes = from m in db.CBAs
select m.Type.Distinct();
return View(myViewModel);
}
then at the top of your view:
#model MYPAGENAMEViewModel
and then later in the view:
MyTypes<br />
#Html.DropDownListFor(model => model.MyTypes.TypeId, (SelectList)model.MyTypes.TypeName)
MyOtherTypes<br />
#Html.DropDownListFor(model => model.MyOtherTypes.TypeId, (SelectList)model.MyOtherTypes.TypeName)
I'm not totally sure what you're after but this might be the solution.
First off, you'd create a ViewModel, such as:
public class MyViewModel
{
List<ABC> abcs { get; set; } // Your database values that you're going to loop through
List<WhateverALookupShouldBe> aDropDownValues { get; set; }
List<WhateverBLookupShouldBe> bDropDownValues { get; set; }
// etc
}
Populate those values in your Data or Controller.
If you're looping through a list and submitting values back you'll want an editor for, so make a folder under your view folder called "EditorTemplates". Then add a cshtml file with the same name as your model, e.g. ABC.
On your main view you would have:
#model MyViewModel
#Html.EditorFor(model => model.abcs)
Now in your ABC Editor template you would write something like this for your drop down lists:
#model ABC
#Html.DropDownListFor(model => model.a, new SelectList(Model.aDropDownValues, "DataValueFieldName", "DataTextFieldName"));
When you submit it back you should have the data returned to you.

.net mvc 3 passing related classes from the controller

God my head is killing me.
Ok I have a controller where i want to pass nested data to the view. Which I am doing like so:
namespace helpme.mvc.Controllers
{
public class CategoryController : Controller
{
private HelpMeContext db = new HelpMeContext();
public ViewResult Index()
{
var model = db.Category.Include(c => c.SubCategories).ToList();
return View(model);
}
}
}
But it is not working. SubCategories come out empty even though there are rows in them. Any suggestions?
Using a break point i see that the model is being correctly populated, but nothing is displayed in the view and no error msg.
Here is the view code:
#model IEnumerable<helpme.mvc.Models.Category>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
#Model.First().SubCategories.First().Name // UPDATE, THIS DISPLAYS A VALUE, SO WHY DOES THE CODE BELOW JUST DISPLAY THE FIRST LEVEL (Categories)?
<ul>
#foreach (var c in Model) {
<li>
<ul>
#foreach (var sc in c.SubCategories)
{
#Html.Display(sc.Name)
foreach (var ssc in sc.SubSubCategories)
{
#Html.Display(ssc.Name)
}
}
</ul>
</li>
}
</ul>
For some reason it only displays the first level, as if it did not receive the SubCategories, even though the break-point proves that it did.
And here is the model, just for reference:
public class Category
{
public Category()
{
SubCategories = new List<SubCategory>();
}
public int ID { get; set; }
[StringLength(255, MinimumLength = 1)]
public string Name { get; set; }
public ICollection<SubCategory> SubCategories { get; set; }
}
public class SubCategory
{
public SubCategory()
{
SubSubCategories = new List<SubSubCategory>();
}
public int ID { get; set; }
[Required()]
[StringLength(255, MinimumLength = 1)]
public string Name { get; set; }
public Category Category { get; set; }
public ICollection<SubSubCategory> SubSubCategories { get; set; }
}
Perhaps explicitly type define your model as List<Category>. Suspect it's currently ObjectQuery<Category>? Can you confirm?
As you know, your View is expecting a IEnumerable<helpme.mvc.Models.Category>.
Try explicitly casting your model to the type your View wants.
List<Category> model = db.Category.Include(c => c.SubCategories).ToList();
It's not clear where the root of the problem is: in the data access, or in accessing the objects in the View. Can you load up some dummy Category and SubCat in that method, and pass known good values to your View? That'll help determine where the issue lies.
I would guess that .Include().ToList() is probably not doing what you think it's doing.
SubCategories probably aren't being mapped to the Category objects you pass to the view.
Try mapping them manually with view models:
e.g.
public class PageViewModel
{
public PageViewModel()
{
this.Categories = new List<Category>();
}
public IEnumerable<Category> Categories {get;set;}
}
...etc
And in your code:
var viewModel = new PageViewModel();
foreach (var category in db.Categories)
{
var categoryVM = new CategoryViewModel();
foreach (var subcategory in category.SubCategories)
{
categoryVM.SubCategories.Add(....
}
}
...etc
Do only one level first (to SubCategory) to confirm.
Try to replace
#Html.Display(sc.Name)
with
#sc.Name
Also please remember you can always put breakpoints in view code, and see how the rendering goes.
Hope you find this useful.
-Zs

Asp.Net MVC Handle Drop Down Boxes that are not part of the Model

I have a small form which the user must fill in and consists of the following fields.
Name (Text)
Value (Text)
Group (Group - Is a list of option pulled from a database table)
Now the Model for this View looks like so,
public string Name { get; set; }
public string Value { get; set; }
public int GroupID { get; set; }
Now the view is Strongly Typed to the above model.
What method would one use to populate the drop down list? Since the data is not contained within the Model (It could be contained in the Model) should we be using Temp/View data? A HTML Helper? What would be the ideal way to achieve this.
I use a viewmodel for this with a dictionary (I like mvc contrib's select box) containing all the properties something like:
class LeViewModel {
public string Name { get; set; }
public string Value { get; set; }
public int GroupID { get; set; }
public Dictionary<string, string> Groups {get; set;}
}
Then in your view you'll only need to do
<%: Html.Select(m => m.GroupId).Options(Model.Groups) %>
Hope it helps.
NOTE: this assumes you are using MVC2.
If I ever need a strongly typed drop-down (a list of countries in this case), I use 2 properties on my ViewModel.
public IEnumerable<SelectListItem> Countries { get; set; }
public int CountryID { get; set; }
I do a pre-conversion of my list to an IEnumerable<SelectListItem> in my action using code similar to this. (This assumes a country class with a name and unique ID).
viewModel.Countries = Repository.ListAll().Select(c => new SelectListItem { Text = c.Name, Value = c.ID });
Then in my strongly typed view, I use:
<%= Html.DropDownListFor(model => model.CountryID, Model.Countries) %>
This is great, because when you post back to a strongly typed action (receiving the same viewmodel back), the CountryID will be the ID of the selected country.
The other benefit, if they have an issue with the validation. All you need to do is repopulate the .Countries list, pass the viewmodel back into the view and it will automatically select the the correct value.
I like the following approach: Have a helper class that does this things for you, something like (pseudo):
class DropdownLogic {
public DropdownLogic(MyModel model) { /* blah */ }
public ListOfDropdownItems ToDropdown() {
/* do something with model and transform the items into items for the dropdownlist */
// f.e. set the item that matches model.GroupId already as selected
}
}
Add in your model:
public DropdownLogic Groups { get; set; }
And in your view:
<%=Html.Dropdown(Model.Groups.ToDropdown())%>

Categories

Resources