How do I associate a PartialView which will be used across the app with a Child Action? For example the PartialView could be a login bar at the top of the page which will need to have some associated logic (loading the model etc) which I would normally put in a ChildAction.
However, I don't know what Controller will be used. I think I could create a base Controller class with the ChildAction and then inherit from that but I was hoping there would be a more elegant solution.
The RenderAction method allows for selecting the controller:
http://msdn.microsoft.com/en-us/library/ee839451.aspx
Related
I've been reading a lots of blogs on MVC provided here:
http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog.aspx
However, I am not being able to explain/convience myself/team:
When to use custom control vs. out of box site core controller?
When does the Out of Box Controller gets invoked?
Benifit of custom control vs. out of box controllers?
If we go with out of box, should we include all business logic on Views. Is this testable?
I also looked at below and still not certain:
https://bitbucket.org/demoniusrex/launch-sitecore-mvc-demo
Any help will be appreciated.
Whilst I broadly agree with Kevin Obee's statement I think it's worth reminding ourselves that controllers are being used in two distinct roles in Sitecore:
Page level controller (invoked by item route)
Component level controller (invoked by redering mechanism)
When to use: Custom controller / default Sitecore controller
Page level controller
Any route that matches an item path will by default use the Index action on the Sitecore.Mvc.Controllers.SitecoreController. This action will return a ViewResult based on the layout configuration of the item.
If you have a need for changing this behaviour (e.g. something that impacts the entire page) you can specify a custom controller and action on the item (or the Standard Values for the item). For the custom controller you can either roll your own or subclass the default controller.
Component level controller
For ViewRendering Sitecore renders the Razor views without the need for a specific controller (well I guess it's the page level controller that is in play - but just imagine that Sitecore provides a default controller that gets the model using the mvc.getModel pipeline and feeds it to the Razor view).
For ControllerRendering you provide a custom controller that can execute logic (see Kevin's answer) and provide a model for the view. There is no benefit from subclassing Sitecore.Mvc.Controllers.SitecoreController.
When are controllers invoked
Page level controller
The action on the page level controller is invoked by the routing engine.
Component level controller
The action on a ControllerRendering is invoked as the page view renders.
Benefit of using: Custom controller / default Sitecore controller
The benefit of a custom controller over the default Sitecore controller is that you are in control of the logic. The benefit of using the default Sitecore controller is that Sitecore provides the logic for you.
Should we include all business logic on Views
No. (See Kevin's answer)
My personal view is that the business logic should go in command and query classes that are invoked from the controller class. From these calls you can assemble a strongly typed view model which gets passed to a dumb razor view for rendering.
Ensure that any services that the controller relies on are passed into it via constructor injection using contracts (interfaces) instead of concrete classes and you should end up with solution that is unit testable.
I am currently working with an mvc site where I have a fairly extensive main layout page. It is dependent on data from the database which in turns includes inherent logic as what to include etc on the layout.
Most of my controller actions are rendered within this layout. I am not sure of how to work this. Being used to master pages in web forms, all the logic resides in the master page. I have a couple mechanisms to achieve the common layout logic but looking for the best practise way of achieving such.
Options are:
Replicate logic in all controller actions (not really an option)
Extract the logic in to another class and call it from the controller actions
All controllers inherit from a base controller which as overrides the OnActionExecuting method and us this to perform logic and return the necessary data in the TempData
Use "RenderAction" in the cshtml to call necessary controller actions and extract the layout in to smaller partial views
Any other options open to me or recommendations?
If I understand you correctly, I would use an action partial
#Html.Action("{ActionName}", "{Controller}", new { roleName= "Admin" })
Action Partials call dedicated controller action methods of type
[ChildActionOnly]
public ActionResult _TopNav(string roleName)
This way you can design recurring logic that will propagate throughout your application without replicating.
Even better, if your _Layout handles privilege based link generating, you can pass role id's and control what the end user sees and what their navigation buttons point to.
I'm looking for a best practice for embedding a form on multiple pages as a partial view.
I have a contact form I'm looking to embed on multiple pages on a site. Usually, the form would be on a contact page and the contact model could be the model for the view and use data annotations for validation. However, the view is already strongly typed.
How can I create a reusable form in a partial view and embed it on the page? I'm using N2 on the site, so the pages have to already have a strongly-typed model, but I would be open to extending those objects.
Personally, I recommend using for Html.RenderAction() for cross-cutting concerns such as these.
The handler for your contact form is going to need to exist independently of the page your are currently viewing so you are left with 3 options:
Manually add it to the response of
the current action
Manually add it to the response of
the current controller by way of a
base controller that modifies the
ViewState or ViewModel
Call the RenderAction()
HtmlHelper inside of the current view
Of these 3 options, while the third is technically more costly than 1 and 2 (because it initiates a brand new request), it is also the most maintanaible solution. By calling RenderAction() you have the advantage of being able to completely isolate your contact form from the rest of the view and thus you won't have to worry about hacking it into the current controller responses.
Use RenderPartial if data model for partial view is already in main view's model, in other case use RenderAction (then the action of the partial view will create its view model itself).
I'm trying to embed a small view snippet that steps through a model fragment that works fine when I embed it in a single controller and pass it to a view like so;
Controller:
return View(_entities.formTemplate.ToList());
View:
http://www.pastie.org/666366
The thing is that I want to be able to embed this particular select box in more than just this single action / view, from the googling I've been doing this appears that it should go into a shared view, but I'm not clear then on how I could populate the model within that view from the controller? (or maybe I'm completely missing the purpose for shared views?)
In the other MVC framework I'm accustomed to working with there is the concept of a filter where you can call code before or after any action and mod the model as it passes the controller and goes to the view, is such a thing possible in .net mvc?
Any assistance appreciated.
You'll want to use the HtmlHelper method DropDownList() in order to create a input:
<%= Html.DropDownList("id", new SelectList(formBuilder, "ID", "Name")) %>
You probably want to use a ViewUserControl here.
You have a couple of options if you go that route. If it's model data that is easily available, recreate it at the call site of your RenderPartial like so:
<%=Html.RenderPartial("ViewName", new ModelData())%>
If it's data that is dependent on the current model data, then you'll need to pass that data somehow to your partial view.
ASP.Net MVC also has the concept of before/after controller actions. You decorate your controller method with an Attribute that derives from ActionFilterAttribute. In there, you have access to OnActionExecuting and OnActionExecuted.
On the main page of my site, I would like to show several views which rely on their own controllers for data retrieval. I do not want to retrieve anything from the DAL in my Home controller.
For example, I want to show view listing top 5 news, a view with random quote from the database, another view with the users shopping cart contents, etc.
After Googling around, I found RenderAction method which is almost perfect, but it's not available in RC1, only in Futures, and apparently, it has some issues.
I found RenderPartial as well, but that relies on the main controller to pass data to the view.
Additional clarification:
The main reason I do not want data access logic in the Home controller is to avoid repeating the code and logic. I will use top 5 news view in several pages/controllers. I do not want to repeat data retrieval in every one of them. I already did separate a lot of logic and validation to business layer. The solution I'm after is RenderAction or UserControls as in classic ASP. I know i can use them in MVC as well, but... whats the point? I mean, if what i'm asking is too complicated or too absurd (reusable UI components), then MVC is definitely not for me, and I'd consider it seriously inferior to classic ASP.NET, because this requirement is really simple.
What you're asking is to basically not perform data access in the HomeController, this seems like a dogmatic approach. I would consider either using RenderAction from the Futures assembly (not sure what's wrong with it, I use it in a number of projects) or SubControllers from MvcContrib.
While I can understand the desire not to replicate functionality in multiple controllers, I don't understand the reluctance to have your Home controller interact with the DAL. I think the partial view is definitely the way to go. My solution to not replicating the functionality would be to push the code that generates the data for the various views into your business or data layer. You could then reference it from each of the required controller actions that use the partial views. Putting it in the business layer could isolate the controller from your data layer, if that's what you desire, but I still think it's the proper job of the controller action to obtain and provide the data to the view.
Another potential solution would be to populate the view generated by your Home controller via Ajax callbacks to the various controller actions that generate the required view components. The drawback to this is that it doesn't fail gracefully in the absence of javascript in the browser.
EDIT
Based on your clarification, I would suggest implementing a base controller that fills the ViewData for the shared controls in ActionExecuted (so that it's done only when the action succeeds). Derive your other controllers from the base controller when you want to inherit this behavior.
If you really don't want to use RenderAction, then the only other option you have is to load the necessary data pieces with action filters. Your home controller could then look like this:
public class HomeController : Controller
{
[RequireNews]
[RequireQuotes]
[RequireCart]
public ActionResult Index()
{
return View();
}
}
These action filters could be re-used where they are needed. You might also choose to place these on the controller class itself.