how to loop through list in a view returned by controller - asp.net-mvc-3

My controller is returning following list :
public ActionResult Index()
{
CRMDataContext mycontext=new CRMDataContext();
var myquery = (from c in mycontext.Websites
select c).ToList();
return View(myquery);
}
i'm wondering how can i loop through myquery to display its properties in view??
any suggestions for improving above code will be added advantage.

#foreach(var item in Model){
...
}
improvement suggestions:
move your query in another method/class for core reusage
separate responsibilities by using separate models for domain and view

You can use foreach loop:
#{
foreach (var element in Model)
{
//Write you html code here.
}
}

First of all it's best that you map your entity into a model and use the model in your view. Most importantly if you will not be using all the fields in your entity.
Suppose your entity has this structure:
public class Website {
public string Title {get;set;}
public string Url {get;set;}
public string Description {get;set;}
}
In your view you can do something like:
#model IEnumerable<Website> // you may need to fully qualify it or add the namespace in your web.config
#foreach(var o in Model){
<div>
<h1>#o.Title</h1>
<p>#o.Description</p>
</div>
}

Related

Foreach ViewBag data gives 'object' does not contain a definition for 'var'

I have a controller that is building a query from Linq to Sql to pass into the ViewBag.products object. The problem is, I cannot loop using a foreach on it as I expected I could.
Here's the code in the controller building the query, with the .ToList() function applied.
var products = from bundles in db.Bundle
join bProducts in db.BundleProducts on bundles.bundleId equals bProducts.bundleID
join product in db.Products on bProducts.productID equals product.productID
join images in db.Images on product.productID equals images.productID
where bundles.bundleInactiveDate > DateTime.Now
select new {
product.productName,
product.productExcerpt,
images.imageID,
images.imageURL
};
ViewBag.products = products.ToList();
Since I am using a different model on the Index.cshtml for other items needed, I thought a simple Html.Partial could be used to include the viewbag loop. I have tried it with the same result with and without using the partial and simply by using the foreach in the index.cshtml. A snippet that includes the partial is below:
<div id="bundle_products">
<!--build out individual product icons/descriptions here--->
#Html.Partial("_homeBundle")
</div>
In my _homeBundle.cshtml file I have the following:
#foreach (var item in ViewBag.products)
{
#item
}
I am getting the ViewBag data, but I am getting the entire list as output as such:
{ productName = Awesomenes Game, productExcerpt = <b>Awesome game dude!</b>, imageID = 13, imageURL = HotWallpapers.me - 008.jpg }{ productName = RPG Strategy Game, productExcerpt = <i>Test product excerpt</i>, imageID = 14, imageURL = HotWallpapers.me - 014.jpg }
What I thought I could do was:
#foreach(var item in ViewBag.Products)
{
#item.productName
}
As you can see, in the output, productName = Awesomenes Game. However, I get the error 'object' does not contain a definition for 'productName' when I attempt this.
How can I output each "field" so to say individually in my loop so I can apply the proper HTML tags and styling necessary for my page?
Do I need to make a whole new ViewModel to do this, and then create a display template as referenced here: 'object' does not contain a definition for 'X'
Or can I do what I am attempting here?
*****UPDATE*****
In my Controller I now have the following:
var bundle = db.Bundle.Where(a => a.bundleInactiveDate > DateTime.Now);
var products = from bundles in db.Bundle
join bProducts in db.BundleProducts on bundles.bundleId equals bProducts.bundleID
join product in db.Products on bProducts.productID equals product.productID
join images in db.Images on product.productID equals images.productID
where bundles.bundleInactiveDate > DateTime.Now
select new {
product.productName,
product.productExcerpt,
images.imageID,
images.imageURL
};
var bundleContainer = new FullBundleModel();
bundleContainer.bundleItems = bundle;
return View(bundleContainer);
I have a model, FullBundleModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace JustBundleIt.Models
{
public class FullBundleModel
{
public IQueryable<Bundles> bundleItems { get; set; }
public IQueryable<Images> imageItems { get; set; }
}
}
and my View now has
#model IEnumerable<JustBundleIt.Models.FullBundleModel>
#foreach (var item in Model)
{
<div class="hp_bundle">
<h3>#Html.Display(item.bundleName)</h3>
</div>
}
If I remove IEnumerable from the model reference, the foreach errors out that there is no public definition for an enumerator.
In the #Html.Display(item.bundleName) it errors out that the model has no definition for bundleName. If I attempt
#foreach(var item in Model.bundleItems)
I get an error that bundleItems is not defined in the model.
So what don't I have wired up correctly to use the combined model?
Do I need to make a whole new ViewModel to do this, and then create a
display template as referenced here...
Darin's answer that you linked states the important concept: Anonymous types are not intended for use across assembly boundaries and unavailable to Razor. I would add that it's rarely a good idea to expose anonymous objects outside of their immediate context.
Creating a view model specifically for view consumption is almost always the correct approach. View models can be reused across views if you are presenting similar data.
It's not necessary to create a display template, but it can be useful if you want to reuse the display logic. HTML helpers can also fill a similar function of providing reusable display logic.
Having said all of that, it's not impossible to pass an anonymous type to a view, or to read an anonymous type's members. A RouteValueDictionary can take an anonymous type (even across assemblies) and read its properties. Reflection makes it possible to read the members regardless of visibility. While this has its uses, passing data to a View is not one of them.
More reading:
Can I pass an anonymous type to my ASP.NET MVC view?
Dynamic Anonymous type in Razor causes RuntimeBinderException
Why not create a new model that contains all of the data that you need?
Example One:
public class ContainerModel
{
public IQueryable<T> modelOne;
public IQueryable<T> modelTwo;
}
This will allow you to access either of your queries in Razor:
#model SomeNamespace.ContainerModel
#foreach (var item in Model.modelOne)
{
//Do stuff
}
I personally avoid using ViewBag at all and store everything I need in such models because it's NOT dynamic and forces everything to be strongly typed. I also believe that this gives you a clearly defined structure/intent.
And just for clarity's sake:
public ViewResult Index()
{
var queryOne = from p in db.tableOne
select p;
var queryTwo = from p in db.tableTwo
select p;
var containerModel = new ContainerModel();
containerModel.modelOne = queryOne;
containerModel.modelTwo = queryTwo;
return View(containerModel);
}
Example Two:
public class ContainerModel
{
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")] //Format: MM/dd/yyyy (Date)
public Nullable<DateTime> startDate { get; set; }
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")] //Format: MM/dd/yyyy (Date)
public Nullable<DateTime> endDate { get; set; }
public SelectList dropdown { get; set; }
public IQueryable<T> modelOne { get; set; }
public IQueryable<T> modelTwo { get; set; }
}
In this case you've stored 3 other items in the model with your 2 queries. You can use the Html helpers to create a Drop Down List in Razor:
#Html.DropDownList("dropdown", Model.dropdown)
And you can use the DisplayFor helper to display your dates as defined in your model with Data Annotations:
#Html.DisplayFor(a => a.startDate)
This is advantageous IMO because it allows you to define all of the data that you want to make use of in your View AND how you plan to format that data in a single place. Your Controller contains all of the business logic, your Model contains all of the data/formatting, and your View is only concerned with the content of your page.

multiple models to partial view

I am teaching myself asp .net mvc3.
I have a partial view which uses various models and lookup type db. I want to keep it strongly typed and use it in multiple places but I am not sure how to implement it.
The example below should explain it better. This question might get a bit long and I appreciate your patience.
The partial view basically gives a small description of the property. A snippet of the ‘_PropertyExcerptPartial’ is below:
#model Test.ViewModels.PropertyExcerptViewModel
<div>
<h3>#Html.DisplayFor(model => model.Property.NoOfBedrooms) bedroom #Html.DisplayFor(model => model.FurnitureType.FurnitureTypeDescription) flat to Rent </h3>
…
</div>
I want to keep this partial view strongly typed and use it in multiple places. The model that it is strongly typed to is as follows:
public class PropertyExcerptViewModel
{
public Property Property { get; set; }
public FurnitureType FurnitureType { get; set; }
}
The two 2 database that this model looks up is as follows:
public class Property
{
public int PropertyId {get; set; }
...
public int NoOfBedrooms {get; set;}
public int FurnishedType { get; set; }
...
}
public class FurnishedType
{
public int FurnishedTypeId { get; set; }
public string FurnishedTypeDescription { get; set; }
}
The furnished type database is basically just a lookup table with the following data:
1 - Furnished
2 - Not Furnished
3 - Part Furnished
4 - Negotiable
I have many such lookups in that I only store an int value in the property database which can be used to look up the description. These databases are not linked to property database and the value of furniture type is read via a function GetFurnitureType(id). I pass stored int value of Property.FurnitureType as the id.
However, I encounter a problem when I try to use this partial view as I am not sure how to pass these multiple models from a view to partial view.
Say I am trying to create an ‘added property’ page. This page basically list the properties added by the logged in user. To facilitate this, I have created another function called GetAddedProperties(userId) that return the properties added by a particular user. In the ‘added property’ view, I can call a foreach function to loop through all the properties returned by GetAddedProperties and display the _PropertyExcerptPartial. Something like this:
<div>
#foreach (var item in //Not sure what to pass here)
{
#Html.Partial("_PropertyExcerptPartial",item)
}
</div>
However, I can’t use the partial view to display information as it will display the int value of furniture type stored in the property database and I am not sure how to get the corresponding FurnitureTypeDescription and pass it to the partial view from the ‘added property’ page.
Any help would be appreciated. Thanks a lot!
You should start by designing a real view model, not some class that you suffix its name with ViewModel and stuff your domain models inside. That's not a view model.
So think of what information you need to work with in your view and design your real view model:
public class PropertyExcerptViewModel
{
public int NoOfBedrooms { get; set; }
public string FurnishedTypeDescription { get; set; }
}
and then adapt your partial to work with this view model:
#model Test.ViewModels.PropertyExcerptViewModel
<div>
<h3>
#Html.DisplayFor(model => model.NoOfBedrooms)
bedroom
#Html.DisplayFor(model => model.FurnitureTypeDescription)
flat to Rent
</h3>
...
</div>
OK, now that we have a real view model let's see how we could populate it. So basically the main view could be strongly typed to a collection of those view models:
#model IEnumerable<Test.ViewModels.PropertyExcerptViewModel>
<div>
#foreach (var item in Model)
{
#Html.Partial("_PropertyExcerptPartial", item)
}
</div>
and the last bit of course is the main controller that will do all the db querying and building of the view model that will be passed to the main view:
[Authorize]
public ActionResult SomeAction()
{
// get the currently logged in username
string user = User.Identity.Name;
// query the database in order to fetch the corresponding domain entities
IEnumerable<Property> properties = GetAddedProperties(user);
// Now let's build the view model:
IEnumerable<PropertyExcerptViewModel> vm = properties.Select(x => new PropertyExcerptViewModel
{
NoOfBedrooms = x.NoOfBedrooms,
FurnishedTypeDescription = GetFurnitureType(x.FurnishedType).FurnishedTypeDescription
});
// and finally pass the view model to the view
return View(vm);
}
Be careful with the lazy nature of EF if that is the ORM that you are using in order not to fall into the SELECT N + 1 trap.

MVC3 Razor - Models and Views

I have an action that creates a List and returns it to my view..
public ActionResult GetCustomers()
{
return PartialView("~/Views/Shared/DisplayTemplates/Customers.cshtml", UserQueries.GetCustomers(SiteInfo.Current.Id));
}
And in the "~/Views/Shared/DisplayTemplates/Customers.cshtml" view I have the following:
#model IEnumerable<FishEye.Models.CustomerModel>
#Html.DisplayForModel("Customer")
Then I have in the "~/Views/Shared/DisplayTemplates/Customer.cshtml" view:
#model FishEye.Models.CustomerModel
#Model.Profile.FirstName
I am getting the error:
The model item passed into the dictionary is of type System.Collections.Generic.List`1[Models.CustomerModel]', but this dictionary requires a model item of type 'Models.CustomerModel'.
Shouldn't it display the Customer.cshtml for every item in the collection in the Customers.cshtml?
Help!
I am not sure why you are calling a partial view like this. If it is a Customer Specific view, why not put it under Views/Customer folder ? Remember ASP.NET MVC is more of Conventions. so i would always stick with the conventions (unless abosultely necessary to configure myself) to keep it simple.
To handle this situation, i would do it in this way,
a Customer and CustomerList model/Videmodel
public class CustomerList
{
public List<Customer> Customers { get; set; }
//Other Properties as you wish also
}
public class Customer
{
public string Name { get; set; }
}
And in the action method, i would return an object of CustomerList class
CustomerList customerList = new CustomerList();
customerList.Customers = new List<Customer>();
customerList.Customers.Add(new Customer { Name = "Malibu" });
// you may replace the above manual adding with a db call.
return View("CustomerList", customerList);
Now there should be a view called CustomerList.cshtml under Views/YourControllerName/ folder. That view should look like this
#model CustomerList
<p>List of Customers</p>
#Html.DisplayFor(x=>x.Customers)
Have a view called Customer.cshtml under Views/Shared/DisplayTemplates with this content
#model Customer
<h2>#Model.Name</h2>
This will give you the desired output.
Your view is expecting a single model:
#model FishEye.Models.CustomerModel // <--- Just one of me
You're passing it an anonymous List:
... , UserQueries.GetCustomers(SiteInfo.Current.Id) // <--- Many of me
You should change your view to accept the List or determine which item in the list is supposed to be used before passing it into the View. Keep in mind, a list with 1 item is still a list and the View is not allowed to guess.

Can I return List<MyModel> to my View from my Controller? (MVC3)

Can I return List to my View from my Controller, or do I need to wrap my List in another class, just for the sake of returning only a class?
If this is possible, how do I iterate over the list? I'm trying this (Razor), but Visual Studio isn't happy about it:
#model List<QueueItem>
.
.
.
#foreach (item in #Model)
You can do it that way, but I never return my domain objects directly to a view. Normally I have a view model for a view containing only the fields that I need on that view.
Given your scenario I would have this list in a view model:
public class MyViewModel
{
public IEnumerable<QueueItem> QueueItems { get; set; }
}
In your action method you will populate these items from a service/repository call and then this view model would be returned to the view:
public ActionResult List()
{
MyViewModel viewModel = new MyViewModel
{
QueueItems = queueItemRepository.GetAllQueueItems()
};
return View(viewModel);
}
In your view you would loop through the items and do what needs to be done:
#foreach (var item in Model.QueueItems)
{
}
I hope this helps.

Two models in one view in ASP MVC 3

I have 2 models:
public class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
public class Order
{
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
I want edit objects of BOTH classes in SINGLE view, so I need something like:
#model _try2models.Models.Person
#model _try2models.Models.Order
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x=>x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
This, of course, don't work: Only one 'model' statement is allowed in a .cshtml file. May be there is some workaround?
Create a parent view model that contains both models.
public class MainPageModel{
public Model1 Model1{get; set;}
public Model2 Model2{get; set;}
}
This way you can add additional models at a later date with very minimum effort.
To use the tuple you need to do the following, in the view change the model to:
#model Tuple<Person,Order>
to use #html methods you need to do the following i.e:
#Html.DisplayNameFor(tuple => tuple.Item1.PersonId)
or
#Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id }) |
Item1 indicates the first parameter passed to the Tuple method and you can use Item2 to access the second model and so on.
in your controller you need to create a variable of type Tuple and then pass it to the view:
public ActionResult Details(int id = 0)
{
Person person = db.Persons.Find(id);
if (person == null)
{
return HttpNotFound();
}
var tuple = new Tuple<Person, Order>(person,new Order());
return View(tuple);
}
Another example : Multiple models in a view
Another option which doesn't have the need to create a custom Model is to use a Tuple<>.
#model Tuple<Person,Order>
It's not as clean as creating a new class which contains both, as per Andi's answer, but it is viable.
If you are a fan of having very flat models, just to support the view, you should create a model specific to this particular view...
public class EditViewModel
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
Many people use AutoMapper to map from their domain objects to their flat views.
The idea of the view model is that it just supports the view - nothing else. You have one per view to ensure that it only contains what is required for that view - not loads of properties that you want for other views.
ok, everyone is making sense and I took all the pieces and put them here to help newbies like myself that need beginning to end explanation.
You make your big class that holds 2 classes, as per #Andrew's answer.
public class teamBoards{
public Boards Boards{get; set;}
public Team Team{get; set;}
}
Then in your controller you fill the 2 models. Sometimes you only need to fill one. Then in the return, you reference the big model and it will take the 2 inside with it to the View.
TeamBoards teamBoards = new TeamBoards();
teamBoards.Boards = (from b in db.Boards
where b.TeamId == id
select b).ToList();
teamBoards.Team = (from t in db.Teams
where t.TeamId == id
select t).FirstOrDefault();
return View(teamBoards);
At the top of the View
#model yourNamespace.Models.teamBoards
Then load your inputs or displays referencing the big Models contents:
#Html.EditorFor(m => Model.Board.yourField)
#Html.ValidationMessageFor(m => Model.Board.yourField, "", new { #class = "text-danger-yellow" })
#Html.EditorFor(m => Model.Team.yourField)
#Html.ValidationMessageFor(m => Model.Team.yourField, "", new { #class = "text-danger-yellow" })
And. . . .back at the ranch, when the Post comes in, reference the Big Class:
public ActionResult ContactNewspaper(teamBoards teamboards)
and make use of what the model(s) returned:
string yourVariable = teamboards.Team.yourField;
Probably have some DataAnnotation Validation stuff in the class and probably put if(ModelState.IsValid) at the top of the save/edit block. . .
In fact there is a way to use two or more models on one view without wrapping them in a class that contains both.
Using Employee as an example model:
#model Employee
Is actually treated like.
#{ var Model = ViewBag.model as Employee; }
So the View(employee) method is setting your model to the ViewBag and then the ViewEngine is casting it.
This means that,
ViewBag.departments = GetListOfDepartments();
return View(employee);
Can be used like,
#model Employee
#{
var DepartmentModel = ViewBag.departments as List<Department>;
}
Essentially, you can use whatever is in your ViewBag as a "Model" because that's how it works anyway. I'm not saying that this is architecturally ideal, but it is possible.
Just create a single view Model with all the needed information in it, normaly what I do is create a model for every view so I can be specific on every view, either that or make a parent model and inherit it. OR make a model which includes both the views.
Personally I would just add them into one model but thats the way I do it:
public class xViewModel
{
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
#model project.Models.Home.xViewModel
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x => x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
You can use the presentation pattern http://martinfowler.com/eaaDev/PresentationModel.html
This presentation "View" model can contain both Person and Order, this new
class can be the model your view references.
Another way that is never talked about is
Create a view in MSSQL with all the data you want to present. Then use LINQ to SQL or whatever to map it. In your controller return it to the view. Done.
you can't declare two model on one view, try to use Html.Action("Person", "[YourController]") & Html.Action("Order", "[YourController]").
Good luck.
Beside of one view model in asp.net you can also make multiple partial views and assign different model view to every view, for example:
#{
Layout = null;
}
#model Person;
<input type="text" asp-for="PersonID" />
<input type="text" asp-for="PersonName" />
then another partial view Model for order model
#{
Layout = null;
}
#model Order;
<input type="text" asp-for="OrderID" />
<input type="text" asp-for="TotalSum" />
then in your main view load both partial view by
<partial name="PersonPartialView" />
<partial name="OrderPartialView" />
I hope you find it helpfull !!
i use ViewBag For Project and Model for task so in this way i am using two model in single view and in controller i defined viewbag's value or data
List<tblproject> Plist = new List<tblproject>();
Plist = ps.getmanagerproject(c, id);
ViewBag.projectList = Plist.Select(x => new SelectListItem
{
Value = x.ProjectId.ToString(),
Text = x.Title
});
and in view tbltask and projectlist are my two diff models
#{
IEnumerable<SelectListItem> plist = ViewBag.projectList;
}
#model List

Resources