How do you guys update (let's say) two partials in a controller action going to the View? For example, you have a left nav partial for links and on the right is your main content. Your main content has a ViewModel that it binds to. Do you guys have a seperate ViewModel for your nav page? If so, do you then create a bigger ViewModel each time that has the main content view model and the left nav ViewModel?
I would have one ViewModel for the nav page (e.g. CategoryListViewModel) and one for the content page (e.g. ProductListViewModel). Then load the CategoryListViewModel in a base controller:
public abstract class BaseController : Controller
{
var _categoryRepository = new CategoryRepository();
protected BaseController()
{
ViewData["Categories"] = _categoryRepository.GetCategories();
}
}
public class ProductsController : BaseController
{
var _productRepository = new ProductRepository();
public ActionResult Index()
{
return View(_productRepository.GetProducts());
}
}
and then in the MasterPage read the categories from the ViewData, and in the content page read the products from the Model on the ViewPage.
You can break the page into different independent sections, have a look at this
page here. Here you have to use Html.RenderAction() for which you have to
download the ASP.NET MVC Futures assembly from codeplex.
Actually I was eventually eluding to this solution. Thanks guys for your input!
Related
I have been running an Umbraco v7 site for about 3 years. Traditionally I have not rendered any pages using controllers, I have however set up some controllers that I POST forms to but that is about it. My existing View pages have logic baked into them and use external helper methods instead of controllers.
I am about to develop a new page and thought this would be my time to test a better design pattern. This page will render data from a controller as well as required form submission via a controller. What is the best route to proceed that will also go smoothly if I decide to do a refactor on my existing View pages?
I am more specifically looking for an answer around Render vs Surface controllers and which one would be better.It is my understanding that my routing would be unchanged if I went with a Render controller, but if I went with surface, I would have to have special routing?
But if I used a render controller, this does not support form submission?
Not sure what else I am missing?
Thanks Again,
Devin
You don't have to configure any special routing - everything is baked right in to Umbraco.
As a rule of thumb Surface Controllers are best used for reusable actions, custom controllers (Route Hijacking) are better for adding custom logic to whole pages (Document Types/Templates) in Umbraco.
Both approaches will enable you to accomplish exactly the same results - the only difference between them is abstraction.
Surface controllers are MVC Child Actions that inherit from Umbraco.Web.Mvc.SurfaceController - this adds helpful Umbraco specific properties and methods.
Surface Controllers are good for creating reusable things like forms or anywhere you need a partial to do anything complicated (i.e. backed by a controller). Have a look at the documentation here.
When you use a custom controller to change how pages are rendered that's called Route Hijacking
To do this you create your own controller than inherits from Umbraco.Web.Mvc.RenderMvcController like this:
public class HomeController : Umbraco.Web.Mvc.RenderMvcController
{
public ActionResult MobileHomePage(RenderModel model)
{
//Do some stuff here, the return the base Index method
return base.Index(model);
}
}
This is a custom controller for the "Home" Document Type. You can of course return a custom model that inherits from RenderModel with your own properties and methods.
Fuller examples and documentation can be found here.
Post Requests
Both options allow you to handle POST requests by adding the [httppost] attribute like so:
Surface Controller:
public class YourSurfaceController: SurfaceController
{
public ActionResult YourAction()
{
// Do stuff
}
[HttpPost]
public ActionResult YourAction()
{
// Do stuff on POST
}
}
Controller for route Hijacking:
public class HomeController : Umbraco.Web.Mvc.RenderMvcController
{
public ActionResult MobileHomePage(RenderModel model)
{
//Do some stuff here, the return the base Index method
return base.Index(model);
}
[HttpPost]
public ActionResult MobileHomePage(RenderModel model)
{
//Do some stuff on POST, the return the base Index method
return base.Index(model);
}
}
I'm working on a new MVC/Razor website, and we've gotten to the point where we need to put in a real menu structure. Currently, our menus are hardcoded into the global shared layout, we need to populate the menus from code, so that we can determine, after login, which menus will be displayed for a user.
We'll want the menus to appear on pretty much every page, except for login and a few other special cases. So we'll continue to one them to be rendered in the shared layout.
It looks simple enough to create an #Html.DisplayFor template that would render a menu item. The question is, how to get data to it?
The shared layout is used with quite a number of views, handling a number of different models, being loaded from a number of controllers.
Adding a List member to each model, and then populating it in each controller, seems tedious, bothersome, and error prone.
Or we could skip adding the collection to each model, and instead have each controller stick it in the ViewBag. That doesn't seem all that great, either.
The only possibility I've been able to dream up, to keep from having to repeat this for every controller, is to define a common base class, derived from Controller, that all of the controllers that use the shared layout could derive from, in turn, that would stuff the MenuItem collection into the ViewBag.
But I'm wondering if I'm missing something. Is there some preferred way of dealing with this situation?
Shared/base view models is a way to go as you mentioned, but in my opinion its not very "single responsibility". Having to inherit from a base model and add menu items on each page is tedious as you mentioned.
If I was you I would use:
#Html.Action("action", "controller")
https://msdn.microsoft.com/en-us/library/ee703457.aspx
This would call an action method, and then you can bind a specific menu model and also return a specific partial view that would be rendered in the view that called it
#Html.Action("TopMenu", "Controller")
[ChildActionOnly]
public PartialViewResult TopMenu()
{
return PartialView(new MenuModel());
}
Create a TopMenu.cshtml
You can even go as far as passing in values into the controller action to modify the output model/view.
You can call #Html.Action from your layout/shared view.
EDIT
Added [ChildActionOnly] as highlighted in comment as this prevents access to the action unless it was called from a parent action and should be used here
If I understand your question correctly...
I prefer to have a MasterViewModel that every ViewModel for every page inherits from. The MasterViewModel holds things common to every page, like the user's name, navigation rules, etc. The shared layout then uses #model MasterViewModel, which is populated by an ActionFilterAttribute.
public class MasterViewModel
{
public IEnumerable<string> NavItems {get; set;}
}
public class HomeIndexViewModel : MasterViewModel
{
// stuff for the home view
}
I then have a MasterViewModelAttribute:
public class MasterViewModelAttribute : ActionFilterAttribute
{
[Inject]
public IDatabase Database { get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
ViewResultBase viewResult = filterContext.Result as ViewResultBase;
// not a view result
if (viewResult == null)
{
return;
}
MasterViewModel model = viewResult.ViewData.Model as MasterViewModel;
// not a MasterViewModel view model
if (model == null)
{
return;
}
model.NavItems = // set your nav items
}
}
Then, every controller derives from a base controller, which implements the attribute:
[MasterViewModel]
public class BaseController : Controller
{
}
And for each controller:
public class HomeController : BaseController
{
public ActionResult Index()
{
return View(new HomeIndexViewModel());
}
}
And finally, the layout:
#model MasterViewModel
// omitted
<div class="nav">
#foreach (var item in Model.NavItems)
{
<a></a>
}
</div>
The thing I like about the pattern is that you still get strong type-checking by using ViewModels (as opposed to the ViewBag), and you don't have to deal with all the extra nav stuff when you want to unit test an action, for example.
This also has an advantage over using partial views and #Html.Action() in that you don't incur a second request to the server for every page load.
Being a newbie, please excuse my trivial question. (ASP.Net MVC4)
After reading lots of posts/tutorials I am still not sure about how to approach. What I am trying to do is have three Dropdownlists on my Index page. For instance, Country, Company, Branch. I have same data model for them which are linked to my database using EF. I have managed to just list all the data on the Index Page. However I need them inside the dropdownlist.
Question is how do I create the controller for that? Currently the controller looks like:
public class HomeController : Controller
{
private CorpCostEntities db = new CorpCostEntities();
public ActionResult Index()
{
return View();
}
...
...
}
Do I need to create separate ActionResult methods for each table to populate data? Or do I use method overload for Index(), such as Index(Country country), Index(Company comp), Index(Branch branch)?
Next question is since there will be difefernt ActionResult methods, do I need to create separate Views for that? I am using simple MVC structure. I don't have ViewModel or something other than MVC.
What exactly the approach to first create the controller for a multiple dropdownlists and then show them in an existing View page?
Thanks for help in advance.
No, you shouldn't need a separate action for each type, I would just toss those values in the ViewBag and read in the view (Here is how to load it into dropdown element):
public class HomeController : Controller
{
private CorpCostEntities db;
public HomeController(){
db = new CorpCostEntities();
ViewBag.CountryList = db.GetCountryList();
ViewBag.CompanyList = db.GetCompanyList();
ViewBag.BranchList = db.GetBranchList();
}
public ActionResult Index()
{
return View();
}
}
Regarding "Next question is since there will be difefernt ActionResult methods, do I need to create separate Views for that?", I am kind of unclear as to what you are asking, but I will give it a go on assumptions....Think of your (GET)ActionResults as pages (your action methods aren't just locked into returning 'pages', but bear with me). Say you have a Action "index" that displays a list of Customers, you would have your Index Action and Index view. Now, add an Edit screen, that will be another Action, Edit, and View, Edit.cshtml.
You might also want to consider to create a ViewModel (CorpCastViewModel) that contains all objects needed to render the view.
You would then have a propertie for the actual CorpCostModel, but also properties for your lookup collections and other stuff you need in your view.
Than you can create an editor as follows:
#Html.EditorFor(model => model.CorpCostModel.SomeField)
and a listbox as
#Html.DropDownListFor(model => model.CorpCostModel.CorpCountryId, new SelectList(Model.CountryList, "CountryId", "CountryName"), "")
I have a partial view that displays a search options like search by category, region, date etc. This exists on every page. The partial uses a viewmodel containing lists of regions, cats etc.
As this is being used on every page - I have to load these properties on the viewmodel in every action in my controllers to ensure the data is available to the partial view. Not that happy with that. (Have simply used inherited viewmodels)
I see the partial can call a renderaction method on the controller to get the data, but now I would have a view calling data from a controller - breaking the mvc pattern.
what are other people doing in this situation?
You can use custom ActionFilters to inject common functionality to your actions/controllers to avoid repeating the same code.
For example :
public class RequiresSearchOptions : ActionFilterAttribute {
public override void OnResultExecuting(ResultExecutingContext filterContext){
filterContext.Controller.ViewData["SearchOptions"] =
GetSearchOptions();
//Or manipulate the model :
//YourViewModel m =
// (YourViewModel)filterContext.Controller.ViewData.Model;
//m.SearchOptions = GetSearchOptions();
}
}
And then decorate your actions/controllers.
[RequiresSearchOptions]
public ActionResult Index() {
return View();
}
//or
[RequiresSearchOptions]
public class HomeController : Controller {
//Actions
}
For a while I have used partial requests to render reused widgets. In my opinion they are are a more MVC way of rendering widgets over RenderAction as they don't require the View to know what action is being called.
My partial requests render partial views so your existing code can be easily migrated. They can also be output cached in the same way as any asp.net mvc action.
Hope this helps.
From this question, it looks like it makes sense to have a controller create a ViewModel that more accurately reflects the model that the view is trying to display, but I'm curious about some of the conventions.
Basically, I had the following questions:
I normally like to have one class/file. Does this make sense with a ViewModel if it is only being created to hand off data from a controller to a view?
If a ViewModel does belong in its own file, and you're using a directory/project structure to keep things separate, where does the ViewModel file belong? In the Controllers directory?
That's basically it for now. I might have a few more questions coming up, but this has been bothering me for the last hour or so, and I can seem to find consistent guidance elsewhere.
EDIT:
Looking at the sample NerdDinner app on CodePlex, it looks like the ViewModels are part of the Controllers, but it still makes me uncomfortable that they aren't in their own files.
I create what I call a "ViewModel" for each view. I put them in a folder called ViewModels in my MVC Web project. I name them after the controller and action (or view) they represent. So if I need to pass data to the SignUp view on the Membership controller I create a MembershipSignUpViewModel.cs class and put it in the ViewModels folder.
Then I add the necessary properties and methods to facilitate the transfer of data from the controller to the view. I use the Automapper to get from my ViewModel to the Domain Model and back again if necessary.
This also works well for composite ViewModels that contain properties that are of the type of other ViewModels. For instance if you have 5 widgets on the index page in the membership controller, and you created a ViewModel for each partial view - how do you pass the data from the Index action to the partials? You add a property to the MembershipIndexViewModel of type MyPartialViewModel and when rendering the partial you would pass in Model.MyPartialViewModel.
Doing it this way allows you to adjust the partial ViewModel properties without having to change the Index view at all. It still just passes in Model.MyPartialViewModel so there is less of a chance that you will have to go through the whole chain of partials to fix something when all you're doing is adding a property to the partial ViewModel.
I will also add the namespace "MyProject.Web.ViewModels" to the web.config so as to allow me to reference them in any view without ever adding an explicit import statement on each view. Just makes it a little cleaner.
Separating classes by category (Controllers, ViewModels, Filters etc.) is nonsense.
If you want to write code for the Home section of your website (/) then create a folder named Home, and put there the HomeController, IndexViewModel, AboutViewModel, etc. and all related classes used by Home actions.
If you have shared classes, like an ApplicationController, you can put it at the root of your project.
Why separate things that are related (HomeController, IndexViewModel) and keep things together that have no relation at all (HomeController, AccountController) ?
I wrote a blog post about this topic.
I keep my application classes in a sub folder called "Core" (or a seperate class library) and use the same methods as the KIGG sample application but with some slight changes to make my applications more DRY.
I create a BaseViewData class in /Core/ViewData/ where I store common site wide properties.
After this I also create all of my view ViewData classes in the same folder which then derive from BaseViewData and have view specific properties.
Then I create an ApplicationController that all of my controllers derive from. The ApplicationController has a generic GetViewData Method as follows:
protected T GetViewData<T>() where T : BaseViewData, new()
{
var viewData = new T
{
Property1 = "value1",
Property2 = this.Method() // in the ApplicationController
};
return viewData;
}
Finally, in my Controller action i do the following to build my ViewData Model
public ActionResult Index(int? id)
{
var viewData = this.GetViewData<PageViewData>();
viewData.Page = this.DataContext.getPage(id); // ApplicationController
ViewData.Model = viewData;
return View();
}
I think this works really well and it keeps your views tidy and your controllers skinny.
A ViewModel class is there to encapsulate multiple pieces of data represented by instances of classes into one easy to manage object that you can pass to your View.
It would make sense to have your ViewModel classes in their own files, in the own directory. In my projects I have a sub-folder of the Models folder called ViewModels. That's where my ViewModels (e.g. ProductViewModel.cs) live.
There are no good place to keep your models in. You can keep them in separate assembly if the project is big and there are a lot of ViewModels (Data Transfer Objects). Also you can keep them in separate folder of the site project. For example, in Oxite they are placed in Oxite project which contains a lot of various classes too. Controllers in Oxite are moved to separate project and views are in separate project too.
In CodeCampServer ViewModels are named *Form and they are placed in UI project in Models folder.
In MvcPress project they are placed in Data project, which also contains all code to work with database and a bit more (but I didn't recommend this approach, it's just for a sample)
So you can see there are many point of view. I usually keep my ViewModels (DTO objects) in the site project. But when I have more than 10 models I prefer to move them to separate assembly. Usually in this case I'm moving controllers to separate assembly too.
Another question is how to easily map all data from model to your ViewModel. I suggest to have a look at AutoMapper library. I like it very much, it does all dirty work for me.
And I also I suggest to look at SharpArchitecture project. It provides very good architecture for projects and it contains a lot of cool frameworks and guidances and great community.
here's a code snippet from my best practices:
public class UserController : Controller
{
private readonly IUserService userService;
private readonly IBuilder<User, UserCreateInput> createBuilder;
private readonly IBuilder<User, UserEditInput> editBuilder;
public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
{
this.userService = userService;
this.editBuilder = editBuilder;
this.createBuilder = createBuilder;
}
public ActionResult Index(int? page)
{
return View(userService.GetPage(page ?? 1, 5));
}
public ActionResult Create()
{
return View(createBuilder.BuildInput(new User()));
}
[HttpPost]
public ActionResult Create(UserCreateInput input)
{
if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");
if (!ModelState.IsValid)
return View(createBuilder.RebuildInput(input));
userService.Create(createBuilder.BuilEntity(input));
return RedirectToAction("Index");
}
public ActionResult Edit(long id)
{
return View(editBuilder.BuildInput(userService.GetFull(id)));
}
[HttpPost]
public ActionResult Edit(UserEditInput input)
{
if (!ModelState.IsValid)
return View(editBuilder.RebuildInput(input));
userService.Save(editBuilder.BuilEntity(input));
return RedirectToAction("Index");
}
}
We throw all of our ViewModels in the Models folder (all of our business logic is in a separate ServiceLayer project)
Personally I'd suggest if the ViewModel is anything but trivial then use a separate class.
If you have more than one view model then I suggest it make sense to partition it in at least a directory. if the view model is later shared then the name space implied in the directory makes it easier to move to a new assembly.
In our case we have the Models along with the Controllers in a project separate from the Views.
As a rule of thumb, we've tried to move and avoid most of the ViewData["..."] stuff to the ViewModel thus we avoid castings and magic strings, which is a good thing.
The ViewModel as well holds some common properties like pagination information for lists or header information of the page to draw breadcrumbs and titles. At this moment the base class holds too much information in my opinion and we may divide it in three pieces, the most basic and necessary information for 99% of the pages on a base view model, and then a model for the lists and a model for the forms that hold specific data for that scenarios and inherit from the base one.
Finally, we implement a view model for each entity to deal with the specific information.
code in the controller:
[HttpGet]
public ActionResult EntryEdit(int? entryId)
{
ViewData["BodyClass"] = "page-entryEdit";
EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
return View(viewMode);
}
[HttpPost]
public ActionResult EntryEdit(Entry entry)
{
ViewData["BodyClass"] = "page-entryEdit";
#region save
if (ModelState.IsValid)
{
if (EntryManager.Update(entry) == 1)
{
return RedirectToAction("EntryEditSuccess", "Dictionary");
}
else
{
return RedirectToAction("EntryEditFailed", "Dictionary");
}
}
else
{
EntryEditViewModel viewModel = new EntryEditViewModel(entry);
return View(viewModel);
}
#endregion
}
code in view model:
public class EntryEditViewModel
{
#region Private Variables for Properties
private Entry _entry = new Entry();
private StatusList _statusList = new StatusList();
#endregion
#region Public Properties
public Entry Entry
{
get { return _entry; }
set { _entry = value; }
}
public StatusList StatusList
{
get { return _statusList; }
}
#endregion
#region constructor(s)
/// <summary>
/// for Get action
/// </summary>
/// <param name="entryId"></param>
public EntryEditViewModel(int? entryId)
{
this.Entry = EntryManager.GetDetail(entryId.Value);
}
/// <summary>
/// for Post action
/// </summary>
/// <param name="entry"></param>
public EntryEditViewModel(Entry entry)
{
this.Entry = entry;
}
#endregion
}
projects:
DevJet.Web ( the ASP.NET MVC web
project)
DevJet.Web.App.Dictionary ( a
seperate Class Library project)
in this project, i made some folders like:
DAL,
BLL,
BO,
VM (folder for view models)
Create a view model base class which has commonly required properties like result of the operation and contextual data ,you can also put current user data and roles
class ViewModelBase
{
public bool HasError {get;set;}
public string ErrorMessage {get;set;}
public List<string> UserRoles{get;set;}
}
In base controller class have a method like PopulateViewModelBase() this method will fill up the contextual data and user roles.
The HasError and ErrorMessage , set these properties if there is exception while pulling data from service/db. Bind these properties on view to show error.
User roles can be used to show hide section on view based on roles.
To populate view models in different get actions , it can be made consistent by having base controller with abstract method FillModel
class BaseController :BaseController
{
public PopulateViewModelBase(ViewModelBase model)
{
//fill up common data.
}
abstract ViewModelBase FillModel();
}
In controllers
class MyController :Controller
{
public ActionResult Index()
{
return View(FillModel());
}
ViewModelBase FillModel()
{
ViewModelBase model=;
string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString();
try
{
switch(currentAction)
{
case "Index":
model= GetCustomerData();
break;
// fill model logic for other actions
}
}
catch(Exception ex)
{
model.HasError=true;
model.ErrorMessage=ex.Message;
}
//fill common properties
base.PopulateViewModelBase(model);
return model;
}
}