If I have an elaborate repeating layout which I would like to only define once, say some fancy grid of boxes, but the content of those boxes would vary both in view and model from grid to grid (but not within a grid), e.g. one page has a fancygrid of product summaries and another page has a fancygrid of category introductions. What's the most sensible pattern for implementing that MVC3?
You could use display templates. For example define a property on your view model that is of type Enumerable<SomeViewModel>:
public class MyViewModel
{
public IEnumerable<SomeViewModel> Models { get; set; }
}
and in the view use the DisplayFor helper:
#model MyViewModel
#Html.DisplayFor(x => x.Models)
then define a custom display template that will automatically be rendered for each element of the Models collection (~/Views/Shared/DisplayTemplates/SomeViewModel.cshtml):
#model SomeViewModel
<div>
#Html.DisplayFor(x => x.SomeProperty)
...
</div>
Templated helpers work by convention. By default it will first look in the ~/Views/CurrentController/DisplayTemplates folder and then in the ~/Views/Shared/DisplayTemplates folder for a template which is named the same way as the type of the collection property (SomeViewModel.cshtml).
You can move the repeating section into a partial view. Then that partial view can be reused anywhere
Action GenreMenu in Store Controller:
[ChildActionOnly]
public ActionResult GenreMenu()
{
var genres = storeDB.Genres.ToList();
return PartialView(genres);
}
in the View this will repeat the partial view three times:
#for (int i = 0; i < 3; i++)
{
Html.RenderAction("GenreMenu", "Store");
}
Related
I have a view that renders 2 partial views. One partial is a list of people. The other partial is a modal edit form to edit a single person. I have a viewmodel that contains a person model and a list of person model. I pass the viewmodel to the view and specify the specific models for the partials. I get an error saying the view requires the PersonModel instead of the PersonViewModel. However, the view needs the viewmodel or it wont work.
The Viewmodel:
public class PersonViewModel
{
public Person Person { get; set; }
public List<Person> PeopleList { get; set; }
}
The view (Index):
#model App.Models.ViewModels.PersonViewModel
<h1>Manage People</h1>
#Html.Partial("_personEditForm", Model.Person)
#Html.Partial("_PersonGrid", Model.PersonList)
The person edit partial calls the Person model:
#model App.Models.Person
and the grid partial calls the same model as a list:
#model List<App.Models.Person>
This works fine if the page has only one partial, but this is the first time I have tried it with 2 and it fails. The Index view must have the ViewModel for the partials to work, so not sure why it wont take it.
I was able to fix this by passing the ViewModel to the view, and passing it to both partials:
#model App.Models.ViewModels.PersonViewModel
<h1>Manage People</h1>
#Html.Partial("_PersonEditForm", Model)
#Html.Partial("_PersonGrid", Model)
Then inside the partials, I specified the models within the viewmodel. For example, in the form partial, the textbox helpers went from:
#Html.TextBoxFor(m => m.FirstName, new { #class = "form-control" })
To
#Html.TextBoxFor(m => m.Person.FirstName, new { #class = "form-control" })
Just wanted to add my 2 cents since this has also been driving me crazy. What I have done is created separate viewmodels for each of the partial views. One similar to PersonEdit and one similar to PersonList.
So you end up with your main VM, and 2 smaller ones.
public class PersonIndexViewModel
{
public PersonEditViewModel Person { get; set; }
public List<PersonListViewModel> PeopleList { get; set; }
}
public class PersonEditViewModel
{
// ...whatever info you want in your edit view
}
public class PersonListViewModel
{
// ... whatever info you want in your table grid
}
Then you call it like so
#model App.Models.ViewModels.PersonViewModel
<h1>Manage People</h1>
#Html.Partial("_PersonEditForm", Model.Person)
#Html.Partial("_PersonGrid", Model.PeopleList)
In my MVC5 project I want to create a menu in a partial view. This menu is dynamic in the sense that it is built from content in my database. Thus I have a controller taking care of creating my menu and returning the menu model to my partial view:
public PartialViewResult GetMenu()
{
MenuStructuredModel menuStructuredModel = menuBusiness.GetStructuredMenu();
return PartialView("~/Views/Shared/MenuPartial", menuStructuredModel);
}
In my partial view called MenuPartial I want to use razor to iterate over my menu items, like:
#model MyApp.Models.Menu.MenuStructuredModel
<div class="list-group panel">
#foreach (var category in Model.ViewTypes[0].Categories)
{
#category.ShownName
}
</div>
Now the problem is the view in which I insert the partial view. If in the view I simply do:
#Html.Partial("MenuPartial")
It won't call the controller to populate the model with data first. What I want is to let the controller return the partial. But I don't know how to do this from the view. In pseudo code I would like to do something like:
#Html.RenderPartialFromController("/MyController/GetMenu")
Thanks to Stephen Muecke and Erick Cortorreal I got it to work.
This is what the controller should look like:
[ChildActionOnly]
public PartialViewResult GetMenu()
{
MenuStructuredModel menuStructuredModel = menuBusiness.GetStructuredMenu();
return PartialView("~/Views/Shared/MenuPartial", menuStructuredModel);
}
And it may called like:
#Html.Action("GetMenu", "Home")
(Hence GetMenu() is declared in the HomeController in my example).
The controller is now called (and the model is populated) prior to the partial view is rendered.
You should use: #Html.RenderAction or #Html.Action.
So I have a Edit view with a partial view. In this partial view I have a selectlist (or dropdownlist) which values come from a ViewBag. In the control I include the selected value but it just does'nt work.
public ActionResult Edit(int id = 0)
{
Customer c = db.Customer.Find(id);
ViewBag.CustomerGlobalQuality = new SelectList(db.GlobalQuality, "Id", "Quality", c.Skill.GlobalQuality);
return View(c);
}
and in the PARTIAL VIEW I have:
#Html.DropDownList("CustomerGlobalQuality")
#Html.ValidationMessageFor(model => model.Skill.GlobalQuality)
what did I miss? It usually works with normal views, so why not with a partial?
if your logic doesn't need partial view, do not include it in this case. Use those lines of code inside your edit chtml view. In your case debug it and see what are you realy sending to drop down list. I see that you included dropdown list dana in edit view. If you use partial view you must pass object to that view in controler.
public ActionResult PartialDDLData()
{
ViewBag.CustomerGlobalQuality = new SelectList(db.GlobalQuality, "Id", "Quality", c.Skill.GlobalQuality);
return Partial("_nameOfView",ViewBag.CustomerGlobalQuality);
}
and make sure your partial view is accessible in shared or controller view folder.
What is the right way to create an input of a different model type for a MVC page? I could just put in the HTML that I needed, but how do you do it the idiomatic way with "Html.EditorFor" etc.
For example, in Stack overflow, you are looking at a post, but at the bottom you are creating an answer. Should an IEnumerable be part of the post model?
You can include both models in the view model for the page. For example, let's say you are viewing a post and want to allow users to comment on the post. Your view model for the page would look like this:
public class ViewPostViewModel
{
// The view model for the post and replies
public PostViewModel Post { get; set; }
// The view model for the comment form
public AddCommentViewModel Comment { get; set; }
}
This is my preferred method of including two view models in the same page, especially if you will need to reuse one of the view models across several different action methods.
Then in your view you can use the standard #Html.EditorFor(m => m.Comment.Message) helpers to create the form inputs, or render the comment form using a partial view.
Use partials views, then you can have something like this:
Your partial:
#model stackoverflow.answer
#HTML.desplaynamefor(u => u.name)
Then in another view you can do this
#model stackoverflow.post
#HTML.displaynamefor(u => u.comment)
#partial("mypartial",new answer())
If you only create a new answer without data coming from he server side (no server side validation, editing, etc.), you could just instantiate the model and then use a partial view for the answer form like this:
QuestionView:
#model ViewModel.Question
#{ var answer = new ViewModel.Answer(); }
<h3>Question:</h3>
#Html.DisplayTextFor(m => m.Text)
#Html.Partial("AnswerFormView", answer)
AnswerFormView:
#model ViewModel.Answer
#using (var form = Html.BeginForm(...))
{
<h3>Answer:</h3>
#Html.EditorFor(m => m.Text)
}
If you however need data from server side (validation, default values, editing, etc.) you'll need to get the data from the controller and use a composition view.
ViewModel.Composed:
public class Composed
{
public ViewModel.Question Question { get; set; }
public ViewModel.Answer Answer { get; set; }
}
ComposedView:
#model ViewModel.Composed
#Html.Partial("QuestionView", Model.Question)
#Html.Partial("AnswerFormView", Model.Answer)
QuestionView:
#model ViewModel.Question
<h3>Question:</h3>
#Html.DisplayTextFor(m => m.Text)
AnswerFormView:
#model ViewModel.Answer
#using (var form = Html.BeginForm(...))
{
<h3>Answer:</h3>
#Html.EditorFor(m => m.Text)
}
I have been able to find lots of examples of adding a Dropdown list to a view but I need to add a dropdown list to a view that also has a Webgrid on it. This entails two different models and from what I see I can only have one per view.
The DDL will be filled from one model and the grid from the other.
I'm just trying to filter the displayed data in the grid with the data selected in the ddl.
Any examples or articles would be greatly appreciated.
TIA
Create a ViewModel that has the data for both your grid and your DropDownList. Use this ViewModel object as the model for your view.
See Steve Michelotti's post for different strategies on implementing the ViewModel pattern.
Something like this, for example:
public class MyViewModel
{
public List<Row> RowsForGrid { get; set; }
public SelectList ItemsForDropdown { get; set; }
}
Since you're trying to filter the displayed data in the grid, I'd do it this way:
In the main view I'd call a partial view. In your case the partial view will hold the DropDownList data. Something like this:
#Html.Partial("DropDownView", ViewBag.DropDownViewModel as DropDownViewModel)
In your controller action you'd fill the DropDownViewModel with the DropDownList data and would pass the DropDownViewModel to the ViewBag like this:
DropDownViewModel dropDownViewModel = new DropDownViewModel();
DropDownViewModel.Items = GetDropDownData(); // Fetch the items...
ViewBag.DropDownViewModel = dropDownViewModel;
ViewModel (DropDownViewModel.cs)
public class DropDownViewModel
{
public SelectList Items { get; set; }
}
Partial View (DropDownView.cshtml)
#model DropDownViewModel
#using (Html.BeginForm("YourControllerAction", "YourControllerName", FormMethod.Get))
{
#Html.Label("Search") #Html.DropDownList("YourDataId", Model.Items, String.Empty)
<input type="submit" value="Search" id="submit"/>
}
"YourDataId" will be a parameter for the action method and will contain the value selected by the user like this:
public virtual ActionResult Index(int? YourDataId, GridSortOptions sort)
{
...
}