Here is my action method from controller.
public class MoviesController : Controller
{
private MovieDBContext db = new MovieDBContext();
// GET: /Movies/
public ActionResult Index()
{
return View( db.Movies.ToList() );
}
}
As it can be seen a List is being passed to View that is why I am expecting to work with it as Listin view but in View it works as IEnumerable
Following is the first line from View
#model IEnumerable<MVCWebApplication2.Models.Movie>
Why passed value that was List behaves as IEnumerable?
Is IEnumerable super class of List?
List<T> implements the IEnumerable<T> interface:
http://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx
It is typically a best practice to try to work with interfaces where possible to keep your code decoupled, so that's why the View was scaffolded like that. In this instance, you could pass any other Movie collection that implements IEnumerable<T> to your View, and your View would happily render without issue.
So, imagine you have another Action in some other Controller like:
public ActionResult Index()
{
Queue<Movie> movies = new Queue<Movie>();
movies.Enqueue(db.Movies.Single(m => m.Title == "Flash Gordon"));
movies.Enqueue(db.Movies.Single(m => m.Title == "Star Wars"));
return View( "Movie/Index", movies );
}
Because Queue<T> implements IEnumberable<T>, movies will get rendered by the same View without having to make any changes to it. If your View was typed to List<Movies>, you would need to create a whole separate View to accommodate your Queue<Movies> model!
As Chris mentioned, List<T> implements IEnumerable<T>, so that is why it is valid for the View's model to be:
#model IEnumerable<MVCWebApplication2.Models.Movie>
Since you said you want to use it as a List, the List also implements IList<T> which should provide you with the functionality you were expecting to have in your view.
You can make use of the List methods like so:
#model IList<MVCWebApplication2.Models.Movie>
<div>
First movie: #Model.First().Name
</div>
#Model.Count Action Movies:
<ul>
#foreach (var item in Model.Where(x => x.Type = "ActionMovie"))
{
<li>#item.Name - #item.Description</li>
}
</ul>
Related
I have a view, which simply does a foreach loop on a IEnumerable passed in the viewmodel, I have a way for people to select these entities on the list (apply a class to them to highlight), and this currently works for some clientside printing through jquery. Is there a way I can get these entities (staff) which have the highlight class and push them back as a model to the controller?
I have tried to use html.hiddenfor and just putting all the staff in there and making a form, with asp-action of my function on the controller and just a submit button, this did not work
the controller contains
public IActionResult StaffSelectionAction(StaffIndexModel model)
{
//things to do to process the selected staff
foreach (Staff staff in model.staff){
//Do stuff
}
return RedirectToAction("Index");
}
the view contains
<form asp-action="StaffSelectionAction">
#Html.HiddenFor(b => b.staff)
<input type="submit" value="Process Staff" class="btn btn-default" asp-action="StaffSelectionAction"/>
</form>
model contains
public IEnumerable<Staff> staff { get; set; }
Edit: spelling mistake in code
There are a few ways to handle this.
One way is to create an helper class, extending the Staff model and adding a 'selectable' attribute for it. Something like:
public class SelectableStaff : Staff
{
public bool Selected {get; set;}
}
Then in your view:
#Html.CheckBoxFor(m => m.Selected)
Using model binding, binding back to the controller with type SelectableStaff should then give you the selected values where you can do something like:
foreach (SelectableStaff staff in model.staff.Where(x => x.Selected)){
//Do stuff
}
You can get it back to Staff easily enough using linq and contains. Alternatively, you can also use #Html.Checkbox("myfriendlyname"), then include a FormCollection in the controller and pull the variable out of it. Personally, I think the model binding is less error prone.
I have a ViewBag that is a list which i create in the controller:
List<string> listoflists = new List<string>();
it then gets filled with some string like for example:
listoflists.Add("java");
listoflists.Add("php");
listoflists.Add("C#");
Adding it to the ViewBag:
ViewBag.listoflists = listoflists;
i then would like to check in my View if this contains a specific string:
#if(ViewBag.listoflists.contains("java")
{
// do html stuff
}
but when running this i get the following error:
System.Collections.Generic.List does not contain a definition for contains
What am i doing wrong, or how should i check if a list contains something in the View?
You might need to cast back to a list of strings:
#if (((IList<string>)ViewBag.listoflists).Contains("java")
{
// do html stuff
}
Also notice that the Contains method starts with a capital C.
So as you can see using ViewBag in ASP.NET MVC is a complete crap leading to very ugly code in your views. For this reason it is strongly recommended to use view models which will contain all the necessary information of a specific view and also being strongly typed.
So cut this ViewBag crap and start using view models:
public class MyViewModel
{
public IList<string> ListOfLists { get; set; }
}
that your controller action can populate and pass to the view:
public ActionResult Index()
{
var model = new MyViewModel();
List<string> listoflists = new List<string>();
listoflists.Add("java");
listoflists.Add("php");
listoflists.Add("C#");
model.ListOfLists = listoflists;
return View(model);
}
and now you can have a strongly typed view to this model which will allow you to avoid the previous casting:
#model MyViewModel
#if (Model.ListOfLists.Contains("java"))
{
// do html stuff
}
So basically every time you use ViewBag/ViewData in an ASP.NET MVC application an alarm should immediately ring in your head telling you: Man, what the hell, you are doing it wrong. Just use a view model in order to avoid transforming your views into an abominable mess of completely irrelevant C# language constructs like casting and stuff. Views are meant for displaying markup.
ViewBag is a dynamic type, it does not know at compile time what is the actual type contained in the key ViewBag.listoflists you defined, you need to cast it to specific type and then you can call those methods on it:
#{
List<string> languages= ViewBag.listoflists as List<string>;
}
#if(languages.contains("java")
{
// do html stuff
}
I have a layout page which has a partial view. The partial view needs to loop through a property on the view model to show a list of categories. When a category is displayed I need to show a list of documents in that category. /Home/Index works, but when I try to view /Documents/Category/{id}, I get an error:
Additional information: The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[ViewModels.DocumentViewModel]', but this dictionary requires a model item of type 'ViewModels.HomeViewModel'.
_Layout.cshtml
...
<body>
#Html.Partial("_CategoryViewModel")
<div class="content">
#RenderBody()
</div>
HomeViewModel.cs
public class HomeViewModel {
...
public ICollection<DocumentCategory> Categories { get; set; }
public ICollection<Documents> Documents { get; set; }
...
}
_CategoryViewModel.cshtml (this should show a list of all categories)
#model ViewModels.HomeViewModel
...
#foreach (DocumentCategory item in Model.Categories)
{
<li>
<a href="#Url.Action("Category", "Documents", new { #id = #item.CategoryId })" title="View documents in the #item.Name category">
<span class="fa fa-files-o"></span> #item.Name
</a>
</li>
}
DocumentsController.cs
public ActionResult Category(int id)
{
var thisCategory = _ctx.Categories.Get(c => c.CategoryId == id).FirstOrDefault();
IEnumerable<DocumentViewModel> docs = null;
if(thisCategory == null)
{
TempData.Add("errorMessage", "Invalid category");
} else {
docs = thisCategory.Documents.ToList();
}
return View("Category", docs);
}
What's happening kind of makes sense - the PartialView on the Layout page needs to enumerate over a collection which isn't present in the ViewModel I'm using. I have no idea how to achieve this - the only way would seem to be to add a Categories property to every ViewModel in my site.
By default, using #Html.Partial() will pass the current model to the partial view, and because your Category.cshtml view uses #model List<DocumentViewModel>, then List<DocumentViewModel> is passed to a partial that expects HomeViewModel.
If you want to render a partial view for HomeViewModel on every page, then use #Html.Action() to call a ChildActionOnly method that returns the partial
[ChildActionOnly]
public ActionResult Categories
{
var model = new HomeViewModel()
{
.... // initialize properties
}
return PartialView("_CategoryViewModel", model)
}
and in the layout
#Html.Action("Categories", yourControllerName)
// or
#{ Html.RenderAction("Categories", yourControllerName); }
As I see it you have a few different alternatives.
1. Use Html.Action and create an Action that returns your view.
#Html.Action("Index", "Category") // Or your controller name.
I believe that there are some performance draw-backs with this approach because the whole MVC lifecycle will run again in order to render the result of the action. But then you can render the result of an action without having the correct model in the view that called it.
One may also argue that this breaks the MVC pattern, but it might be worth it.
2. Use a generic model (or an interface) in your _Layout.cshtml, and let your viewmodels inherit from that model.
In your _Layout.cshtml:
#model IBaseViewModel
And let all your viewmodels implement this interface.
public interface IBaseViewModel
{
ICollection<DocumentCategory> Categories { get; set; }
}
public interface IBaseViewModel<T> : IBaseViewModel
{
T ViewModel {get; set;}
}
Since you're placing #Html.Partial("_CategoryViewModel") in _Layout.cshtml I assume that it should be visible in all pages, so I think it's logical that all the controllers that are using _Layout.cshtml make sure that it gets the information it needs, and thus adding Categories to the model.
I use this approach all the time for stuff like breadcrumbs and menu-information (stuff that is used in all pages). Then I have a basecontroller that makes sure that Categories is populated with the correct info.
This is the view:
#model tgpwebged.Models.sistema_DocType
...
this model is an entity used with textBoxFor and others html helpers
This is the controller.
public ActionResult AdminSettingAddTipo()
{
IEnumerable<string> indices;
using (tgpwebgedEntities context = new tgpwebgedEntities())
{
var obj = from u in context.sistema_Indexes select u.idName;
indices = obj.ToList();
}
return PartialView(indices);
}
I have all I need here, I am using a model to create with the view so I am not allowed to send ´indices´ as a model because it´s not allowed to have 2 models in one view.
I don´t want to use ´Tupe´ now a parent view. I just want to know how is the best way to send my IEnumerable to the view.
I was thinking of ViewBag for the last option but I am avoiding ViewBag.
thanks
ViewBag is not a good choice. Create ViewModel using your list and your current Model:
public class YourViewModel
{
public sistema_DocType Type { get; set; }
public IEnumerable<string> Indices {get;set;}
}
Hope,it will help.
If you don't want to use ViewBag for whatever reason, you could create a Model specifically for the view that contains the info from the old model and the new indices you want. This is a common pattern in MVC development. You can even have the ViewModel be a Decorator for your current Model.
http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx
Use strongly defined where you can, apply this to a model and send that model:
Model
public class MyModel{
public List<sistema_Indexes> indecies {get;set;}
}
Controller
MyModel model = new MyModel();
model.indecies = context.sistema_Indexes.Select(u=> u.idName).ToList();
I'm trying to get a agrip on MVC3 but im failing on a Viewmodel which is supposed to display a List but constantly running into an dictionary error.
The model item passed into the dictionary is of type
"marc_bew_online.ViewModels.StellenlisteViewModel", but this dictionary requires a model item of type "System.Collections.Generic.IEnumerable`1[marc_bew_online.ViewModels.StellenlisteViewModel]".
Here's the code to my repository:
public class Stellenbeschr_Repo : IStellenBeschrRepository
{
marc_bew_entities db = new marc_bew_entities();
public IEnumerable<STELLENBESCHREIBUNGEN> ListAktuell()
{
DateTime dt1 = new DateTime(2011, 1, 1);
var stelleBeschreibungAbDatum = (from stellebeschreibung in db.STELLENBESCHREIBUNGEN
where DateTime.Compare((DateTime)stellebeschreibung.VON_DATUM, dt1) >= 0
select stellebeschreibung).ToList();
return stelleBeschreibungAbDatum;
}
}
Controller + ViewModel:
private IStellenBeschrRepository _repository;
public Default1Controller()
: this(new Stellenbeschr_Repo())
{
}
public Default1Controller(IStellenBeschrRepository repository)
{
_repository = repository;
}
#endregion
public ActionResult Index()
{
return View(_repository.ListAktuell());
}
public ActionResult Stellenliste()
{
var viewModels = new StellenlisteViewModel { StellenListe = _repository.ListAktuell() };
return View(viewModels);
}
public class StellenlisteViewModel
{
public IEnumerable<STELLENBESCHREIBUNGEN> StellenListe { get; set; }
}
Viewpage extract:
#foreach(var item in Model.StellenListe)
{
<tr>
<td>
#Html.Display(item.STELLENBESCHREIBUNG);
</td>
</tr>
}
The Viewpage is currently displaying ";" for every item the LINQ expression has found
I just cant find a solution to get the list displayed in my view.
The problem is most likely your view code.
Make sure your model is declared as:
#model StellenlisteViewModel
and not:
#model IEnumerable<StellenlisteViewModel>
EDIT
From the sounds of it you may be confusing a few things.
Your page will have a single view model. On this view model will be a list of STELLENBESCHREIBUNGEN which you are wanting to display.
To do this, first make sure your view page accepts a single view model:
#model StellenlisteViewModel
Secondly, you want to add a ToList() call in your repository:
var stelleBeschreibungAbDatum = (from stellebeschreibung in db.STELLENBESCHREIBUNGEN
where DateTime.Compare((DateTime)stellebeschreibung.VON_DATUM, dt1) >= 0
select stellebeschreibung).ToList();
Thirdly, your view page will look something like this:
#model StellenlisteViewModel
foreach(var item in Model.StellenListe)
{
// output each individual item to the page
// here you can access the individual properties on your STELLENBESCHREIBUNGEN, e.g.:
<span>#item.Description</span>
}
Sorry, I don't have access to MVC3 currently, so I can't check the syntax.
EDIT#2
You are using Display incorrectly. You may wish to check the documentation for it.
It is also like DisplayFor() is what you are looking for.
Your are returning a single item rather than a list of items. You either need to declare the view to take marc_bew_online.ViewModels.StellenlisteViewModel (rather than an IEnumerable) or return a list of the viewmodel items.