Approaches for handling common data in ASP.NET MVC 3 views - asp.net

I'm struggling with what would seem to be a very simple concept. If I have a value in the ViewBag intended for use by my _Layout.cshtml, how and where do I set that value?
Here are the most obvious (to me) options as I currently see them:
Set the value in each controller (not DRY)
Create my own controller base inheriting from Controller and set the value in the base class
Set the value in Global.asax.cs (feels dirty)
Create an ActionFilter to set the data and register the filter globally (also feels wrong)
Set the value in _ViewStart.cshtml (feels VERY wrong and VERY dirty)
For example:
_Layout.cshtml
<!DOCTYPE html>
<html>
<head runat="server">
<title>#ViewBag.Title</title>
</head>
<body>
<div id="header">
<h1>Welcome #ViewBag.UserName</h2>
</div>
<div id="content">
#RenderBody()
</div>
</body>
</html>
If each controller sets the UserName value, that's not terribly DRY. If I were tackling this with something like CodeIgniter, I'd just create my own base controller to handle these common items and go about my merry way. Is there a more preferred option with ASP.NET MVC 3?

Common view model and base controller is the way to go IMO. Use a common view model as the base class for all of your view models. Use the OnActionExecuted method in the base controller to get the view model (for an action returning a view) and cast it to the common view model. Set the common properties at that time.

Let's say I want to have ViewBag.PageHeight have a default value of 1000 for all pages but then allow that to be overridden by any page/view. This is what I put in the _Layout.cshtml:
The page height is #(ViewBag.PageHeight ?? 1000)
This seems to work.

Related

Can an ASP.NET MVC PartialView have a _Layout

Can an ASP.NET MVC PartialView directly use a Layout?
MSDN
I have looked at MSDN for PartialViewResult and compared with ViewResult:
ViewResult has a property MasterName that PartialViewResult does not.
But.. I can still define the Layout property in the partial views Razor.
Background
I am currently remediating a large code base with a huge amount of partial views used to fill iframes. Ideally they would be converted to normal views (ideally we would not use iframes) but I was wondering about just adding a layout to these partials to take out the <head> element at least so that we have more control over the script versioning (everything is replicated in each partial view). I'm looking for a light-touch solution since much of the code is expected to be thrown away.
Yes, even in Partial View also we can set Layout property which turns Partial View as normal View with Layout.
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Yes.
You can use a layout within a layout:
Layout
View
Layout
PartialView
We have an example of this in our codebase.

Models Interfering with Partials

So currently in my _Layout file, I have a global login and Register system which will show on every page:
<div id="global_login_register">
<div id="login">
#Html.Partial("_LoginPartial")
</div>
<div id="register">
#Html.Partial("_RegisterPartial")
</div>
</div>
#RenderBody()
In my loginPartial page I have a #model LoginModel and in my register I have #model RegisterModel. So far everything works, but the problem is when I try to submit another model to the View() it's interfering with the login partials. Is the only way to fix this is to use the ViewBag and save my model onto there?
If your partial needs a model, you need to pass it with the partial call: #Html.Partial("_LoginPartial", aLoginModel). The harder question is where to get aLoginModel, and there are a few possible options...
Use #Html.Action(...) to defer to a separate control (that can build a LoginModel for you) - generally not worth the overhead of a full controller lifecycle, but it's a clean solution if you can afford it.
Use an interface for your LoginModel and require that every view model implement it - almost certainly not a good idea, but it would work.
Store the LoginModel in ViewBag/ViewData using some mechanism that's guaranteed to execute before your views will be rendered.
Write your own Html helper that knows how to build the LoginModel from the HtmlHelper context. You could either use that result in the _layout (e.g. #Html.Partial("_LoginPartial", Html.LoginModel)) or put the Partial call inside the helper (e.g. #Html.Login()).
For anything performance-critical, I would use #4.

PartialViews in ASP.NET MVC4

I've noticed there are several ways to use Views and PartialViews:
RenderAction, RenderPartial, and "return PartialView"
RenderAction when placed inside HTML, will simply call an Action and Render the View returned (the View returned can be partial view or view?)
RenderPartial will simply retrieve the contents of a View without executing any Controller action
Finally, what's the difference between "Return View" and "Return PartialView"?
Thanks
return View() returns the view with a Layout enabled so you get full HTML page with <html> and <body> tags. return PartialView() on the other hand disables the Layout and you get only the HTML fragment contained in this view. Actually when working with the Razor view engine I prefer to talk about templates and not views and partial views. That's because a view is a template and a partial view is a template without a layout. But in both cases it's a Razor template.

how to display using asp.net mvc

I have a partial view called StudentInfo.ascx. In it I have something like this:
<fieldset>
<legend>Students Information</legend>
<div>
<label>First Name:</label>
<label>Last Name:</label>
</div>
</fieldset>
I have another partial view called Detail.ascx which will need to display this StudentInfo.ascx. I did something like this:
<%= Html.RenderAction("StudentInfo"); %>
Is that right?
It depends on what you intend to do and if you use strong type views etc... By the info you provided it would be better to use RenderPartial() but your partial view doesn't make any sense the way it is. It lacks significant details.
RenderPartial vs. RenderAction
If you use Html.RenderPartial(), it will work faster, but you will have to provide the data for your partial views in a single controller action that returns the parent (partial)view.
If you use Html.RenderAction() it will work slower, but it gives you more flexibility and decoupling from the parent (partial)view, because data for a particular child partial view will be provided by always the same controller action that's completely independent of parent (partial)view and controller action that returned it.
How to choose
Both are correct. It all depends on the problem at hand.
If your partial views are not strongly typed and/or they are meant for posting you will most probably display them using RenderPartial() extension method.
But if they do consume some data and they are strongly typed and they are used on multiple completely different views it's probably much better to use RenderAction().
Think of RenderPartial() as a placeholder that just renders some view and RenderAction() as a separate MVC request pipeline. So before partial view gets rendered the whole request pipline is executed:
request gets issued
controller action gets invoked, prepares data and
returns a partial view (that will be placed inside some other parent (partial)view)
So much more overhead.
Try
<% Html.RenderPartial("StudentInfo") %>

Html.RenderPartial call from masterpage

Here is a scenario: Let's say I have site with two controllers responsible for displaying different type of content - Pages and Articles. I need to embed Partial View into my masterpage that will list pages and articles filtered with some criteria, and be displayed on each page. I cannot set Model on my masterpage (am I right?). How do I solve this task using Html.RenderPartial?
[EDIT]
Yes, I'd probably create separate partial views for listing articles and pages, but still, there is a barrier that I cannot and shouldn't set model on masterpage. I need somehow to say "here are the pages" as an argument to my renderpartial, and also for articles. Entire concept of renderpartial with data from database in masterpages is a bit blurry to me.
How about creating an HtmlHelper extension method that allows you to call a partial view result on the an action on the controller.
Something like
public static void RenderPartialAction<TController>(this HtmlHelper helper, Func<TController, PartialViewResult> actionToRender)
where TController : Controller, new()
{
var arg = new TController {ControllerContext = helper.ViewContext.Controller.ControllerContext};
actionToRender(arg).ExecuteResult(arg.ControllerContext);
}
you could then use this in your master page like
<% Html.RenderPartialAction((HomeController x) => x.RenderPartial()) %>
and in your controller the appropriate method
public PartialViewResult RenderPartial()
{
return PartialView("~/Path/or/View",_homeService.GetModel())
}
Well that is my 2 cents anyway
I had a similar post and came up with an object model to handle it.
I HATE the non-strongly typed views so went with this approach and it is working well.
I think that your solution may lie in the land of MVC 2.0 RC and beyond...
Phil Haack posted an article on his blog: http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx
The ViewData Model property should only be used for the content that you're viewing/editing on the main section of the UI.
Other parts of the view may need some data present in ViewData, so just add those to the dictionary.
I'd just pass data from the dictionary like this: ViewData["articles"] to the partial. (or ViewData.Get() from MvcContrib).
You might also look at the recently implemented SubController pattern implemented in MvcContrib.
yes, this is correct. but let's look at this scenario:
on views that are related to articles, I'd have ViewData["article"], and on views related to pages, I have ViewData["pages"], but I don't have both articles and pages available all time. So, if I add:
Html.RenderPartial("articlesView", ViewData["articles"])
Html.RenderPartial("pagesView", ViewData["pages"])
to my masterpage, I'll have an exception thrown on each page on which ViewDataDictionary doesn't contain both articles and pages.
At least, that's how I see it.
The way that I handle this is by using a BaseViewModel. All Views are strongly typed against a view model that inherits from BaseViewModel.
The BaseViewModel class has all of the information needed by the MasterPage. So for navigation your BaseViewModel may look like this:
public class BaseViewModel
{
public BaseViewModel()
{
NavigationItems = RetrieveNavigationItemsFromModel();
}
public List<NavItems> NavigationItems {get; set;}
}
In your MasterPage and PartialViews, you can cast the Model to BaseViewModel and access the NavigationsItems property.
<ul>
<% foreach (NavItem ni in (Model as BaseViewModel).NavigationItems) { %>
<li>
<%= ni.DisplayText %>
</li>
<% } %>
</ul>
This is a very late reply, but I got to this page whilst googling - so chances are someone else will see this question (and my reply) as well.
The way I've worked around this problem is by using a simple jQuery script to load a PartialView and execute it's controller code. Sample below.
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<script type="text/javascript">
$(document).ready(function() {
$("#applicationForm").load("/Home/ApplicationForm");
});
</script>
<div id="applicationForm" />
</asp:Content>
The big drawback to this approach is that the client has to have scripting enabled for it to work (so it's really SEO unfriendly). If that's something you can live with it works well. I only use it on an intranet site where I know that each client has JavaScript enabled and I don't have to worry about google's bots.

Resources