How to re-use simple static content across MVC views - asp.net-mvc

Apologies if this has been posted before, however, I am struggling to distinguish the basic logic from the many more complex tutorials/samples that exist.
I have some basic static HTML that I'd like to re-use within multiple (but not all) Views:
<div class="logout">
#Html.ActionLink("Log out", "Login", "Login", null, null)
</div>
How do I place this into a reusable component, and how do I then reference it in my Views?
All the other examples I've found today seem to pass Models to the object, or make it doing something dynamic, which is not required.

Just put your static html inside partial view and then include partial view in your view by
using Html Helpers such as #Html.Partial() or #Html.RenderPartial().
Examples :-
#{ Html.RenderPartial("Path/to/my/partial/view"); }
#Html.Partial("Path/to/my/partial/view")

Related

When using ASP.NET MVC, what is the best way to update multiple page sections with one HTML.Action() method?

I have a pretty big ASP.NET MVC site with 100 controllers and thousands of actions. Previously the header image that was defined on the Site.Master page was hardcoded and I want to make it dynamic.
To do so, I added this line to my Site.Master file:
<%= Html.Action("GetHeaderTitle", "Home")%>
which just returns some HTML for the header title such as:
<span style='font-size:15px;'>My Header Title</span>
The issue is that <title> also had this same hard coded value. I could obviously create another HTML.Action to have it show the dynamic valid in the title, but now I am going back to the server twice for essentially the same information (not the exact same HTML as I don't want the span information, but the same logic on the server to get the data).
Is there a way to have an Html.Action return multiple snippets of HTML that I can updates in different places on my master page?
I think you're looking at it wrong - if retrieving of the title is a long operation then just cache the results and write different actions anyway.
// Controller
public string GetTitle()
{
var title = (string)ControllerContext.HttpContext.Items["CachedTitle"];
if (string.IsNullOrEmpty(title))
{
title = "some lengthy retrieval";
ControllerContext.HttpContext.Items["CachedTitle"] = title;
}
return title;
}
public ActionResult GetTitleForTitle()
{
return Content(GetTitle());
}
public ActionResult GetHeaderTitle()
{
return Content("<span>"+ GetTitle() + "<span>");
}
Alternatively, you can cache it directly on the view, which is kind of evil (the simpler view the better):
<%
ViewBag.CachedTitle = Html.Action("GetHeaderTitle", "Home");
%>
...
<%= ViewBag.CachedTitle %>
...
<%= ViewBag.CachedTitle %>
Are you using <asp:ContentPlaceHolder ID="TitleContent" runat="server" /> ? in your site.master? Or have you considered using it? This would allow you to set the title from within a view based on your model.
Maybe you should also consider to introduce ViewModels, which allows you to combine view related data into a ViewModel and return it from a controller. This would allow you to batch queries and save round trips. And use a data repository which gets injected into your controller class (if you are not already doing). Sorry I'm guessing here because you do not show any controller code.
Or you should take advantage of client side code (JavaScript) and load parts of the UI via ajax? And have the UI update itself (could also be reactive).
Unfortunately I have the feeling that the thing your are facing today is not about the title but more about the hundreds of controllers with thousands of actions.
So researching how to organize controllers and building up an opinion based on your use cases would be my best bet based on what I can see and guess from your question.
Hope that helps...

What is a good way to build sub views with logic in ASP.NET MVC 3

We're building a business application using Microsoft ASP.NET MVC 3.
Some views are now becoming so complex that it seems reasonable to divide them into two or more separate views, with separate controllers and models. (Reuse is another reason for wanting to build separate views.)
Which method do you recommend for achieving such separation in ASP.NET MVC?
Partial Views seems like the obvious answer, but as I see it, partial views "out of the box" has little support for separate controllers.
The best solution we've found so far is this, using the
Html.RenderAction("<view initialization action method>")
method as explained here: http://www.primaryobjects.com/CMS/Article129.aspx
This post also mentions the RenderAction method: ASP .NET MVC correct UserControl architecture
Do you see any weaknesses with this approach?
Do you recommend any better, easier ways of achieving this?
Take a "Post Detail" view. A composite view that displays both "Post Summary" and "Post Comments".
Taking the partial approach, you'd end up with:
public class PostDetailModel
{
PostSummaryModel Summary { get;set; }
PostCommentsModel Comments { get;set; }
}
and view:
<div id="post_detail">
#Html.Partial("Summary", Model.Summary)
<ul class="comment-list">
#foreach(var comment in Model.Comments)
{
<li>#Html.Partial("Comment", comment)</li>
}
</ul>
</div>
This means that the PostController.Detail method would be responsible for constructing a PostSummaryModel, constructing a PostCommentsModel and selecting which partials to use to render each.
If you then also had the following model:
public class PostListModel
{
ICollection<PostSummaryModel> Posts { get;set; }
}
You would have two actions responsible for constructing a PostSummaryModel and knowing which partial to use. If your application isn't properly structured, this might lead to duplicate data access/model mapping code. But if you delegate and abstract model construction into re-usable model factories (that could be called by both actions) you minimise this risk.
One the other hand, taking the Html.Action approach your model simply becomes:
public class PostDetailModel
{
int PostId { get;set; }
}
and view:
<div id="post_detail">
#Html.Action("Summary", new { Model.PostId })
#Html.Action("Comments", new { Model.PostId })
</div>
It can then be left up the "Summary" and "Comments" actions to construct their own model and select a view.
There is however an ever so slight performance hit in choosing the Html.Action approach because ASP.NET MVC has to go through the whole process of model-binding, executing action filters, validating, etc so you probably wouldn't use Html.Action to display items in a sufficiently long list view. But for creating a composite view it can be a really clean way to stitch together half a dozen or so existing views.
I think you should go with Action or RenderAction as Darin points out if you want to have logic in your view. However, there is a "hacky" solution that you could use and that is to implement your own IViewPageActivator. Then you could have your views implement a custom WebViewPage and inject services in this WebViewPage and they will be available to your views. To summarize:
Use Partial or RenderPartial if you only need to inject the model
Use Action or RenderAction if you have some logic in your partial view
Implement IViewPageActivator and some other stuff if you really have to
UPDATE: Also, in simple scenarios helpers might do the job for you depending on what you are trying to achieve.
Take a look at client site MVC javascript framework for controlling complex UI.
In combination with jquery templating very powerfull and an option to keep an eye on ...
Backbone.js
http://documentcloud.github.com/backbone/
Kockout.js
http://knockoutjs.com/
Knockout.js vs. Backbone.js – Part 1
http://ifandelse.com/?p=61
Knockout.js vs. Backbone.js – Part 2
http://ifandelse.com/?p=70
Html.Action and Html.RenderAction are the best way to externalize some repeating complex logic in the views that requires its own controllers and models.

How to abstract common snippets of markup with ASP.NET MVC

I have a lot of content-heavy views in my ASP.NET MVC 2 site. These contain several re-occurring HTML patterns. When using ASP.NET Webforms, a class derived from WebControl could encapsulate these patterns. I'd like some pointers on the correct approach for this problem with MVC.
Detailed Explanation
Patterns not unlike the following HTML markup keep occurring throughout these views. The markup renders into an isolated a box of content:
<div class="top container">
<div class="header">
<p>The title</p>
<em>(and a small note)</em>
</div>
<div class="simpleBox rounded">
<p>This is content.</p>
<p><strong>Some more content</strong></p>
</div>
</div>
This is a trivial example, but there are more complex recurring patterns. In ASP.NET Webforms I would have abstracted such code into a WebControl (let's say I'd have named it BoxControl), being included on a page like this:
<foo:BoxControl runat="server">
<Header>The title</Header>
<Note>(and a small note)</Note>
<Content>
<p>This is content.</p>
<p><strong>Some more content</strong></p>
</Content>
</foo:BoxControl>
This abstraction makes it easy to adapt the way the box is constructed throughout the site, by just altering the BoxControl source. It also keeps the static HTML content neatly together in the View Page, even when combining several BoxControls on a page. Another benefit is that the HTML used as content is recognized by the IDE, thus providing syntax highlighting/checking.
To my understanding, WebControls are discouraged in ASP.NET MVC. Instead of a WebControl, I could accomplish the abstraction with a partial view. Such a view would then be included in a View Page as follows:
<%= Html.Partial("BoxControl", new {
Header="The Title",
Note="(and a small note)",
Content="<p>This is content.</p><p><strong>Some more content</strong></p>"});
%>
This is not ideal, since the 'Content' parameter could become very long, and the IDE does not treat it as HTML when passed this way.
Considered Solutions
Strongly-Typed ViewModels can be passed to the Html.Partial call instead of the lengthy parameters shown above. But then I'd have to pull the content in from somewhere else (a CMS, or Resource file). I'd like for the content to be contained in the View Page.
I have also considered the solution proposed by Jeffrey Palermo, but that would mean lots of extra files scattered around the project. I'd like the textual content of any view to be restricted to one file only.
Should I not want to abstract the markup away? Or is there maybe an approach, suitable for MVC, that I am overlooking here? What is the drawback to 'sinning' by using a WebControl?
There is a solution to this problem, although the way to get there is a little more clutsy than other frameworks like Ruby on Rails.
I've used this method to create markup for Twitter Bootstrap's control group syntax which looks like this:
<div class="control-group">
<label class="control-label">[Label text here]</label>
<div class="controls">
[Arbitrary markup here]
</div>
</div>
Here's how:
1) Create a model for the common markup snippet. The model should write markup on construction and again on dispose:
using System;
using System.Web.Mvc;
namespace My.Name.Space
{
public class ControlGroup : IDisposable
{
private readonly ViewContext m_viewContext;
private readonly TagBuilder m_controlGroup;
private readonly TagBuilder m_controlsDiv;
public ControlGroup(ViewContext viewContext, string labelText)
{
m_viewContext = viewContext;
/*
* <div class="control-group">
* <label class="control-label">Label</label>
* <div class="controls">
* input(s)
* </div>
* </div>
*/
m_controlGroup = new TagBuilder("div");
m_controlGroup.AddCssClass("control-group");
m_viewContext.Writer.Write(m_controlGroup.ToString(TagRenderMode.StartTag));
if (labelText != null)
{
var label = new TagBuilder("label");
label.AddCssClass("control-label");
label.InnerHtml = labelText;
m_viewContext.Writer.Write(label.ToString());
}
m_controlsDiv = new TagBuilder("div");
m_controlsDiv.AddCssClass("controls");
m_viewContext.Writer.Write(m_controlsDiv.ToString(TagRenderMode.StartTag));
}
public void Dispose()
{
m_viewContext.Writer.Write(m_controlsDiv.ToString(TagRenderMode.EndTag));
m_viewContext.Writer.Write(m_controlGroup.ToString(TagRenderMode.EndTag));
}
}
}
2) Create a nifty Html helper
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using My.Name.Space
namespace Some.Name.Space
{
public static class FormsHelper
{
public static ControlGroup ControlGroup(this HtmlHelper helper, string labelText)
{
return new ControlGroup(helper.ViewContext, labelText);
}
}
}
3) Use it in the view (Razor code)
#using (Html.ControlGroup("My label"))
{
<input type="text" />
<p>Arbitrary markup</p>
<input type="text" name="moreInputFields" />
}
This is also the way MVC framework renders a form with the Html.BeginForm method
Well you wouldn't render the partial like that, pass it a strongly-typed ViewModel, like this:
<%= Html.RenderPartial("BoxControl", contentModel) %>
contentModel is the ViewModel (just a POCO-like storage mechanism for your views), which the strongly typed partial view would bind to.
So you can do this in your partial view:
<h1><%: Model.Header %></h1>
<p><%: Model.Content %></p>
etc etc
After considering the answers and running an experiment, I'm inclined to adhere to the pure MVC approach and duplicate some presentation code throughout View Pages. I'd like to elaborate on the rationale for that decision.
Partial View
When using a Partial View, The content for the box needs to be passed as a View Model, making the View Page less readable versus declaring the content HTML on the spot. Remember that the content does not come from a CMS, so that would mean filling the View Model with HTML in a controller or setting a local variable in the View Page. Both of these methods fail to take advantage of IDE features for dealing with HTML.
WebControl
On the other hand, a WebControl-derived class is discouraged and also turns out to have some practical issues. The main issue that the declarative, hierarchical style of traditional ASP.NET .aspx pages just does not fit the procedural style of MVC.NET View Pages. You have to choose for either a full blown traditional approach, or go completely MVC.
To illustrate this, the most prominent issue in my experimental implementation was one of variable scope: when iterating a list of products, the MVC-way is to use a foreach loop, but that introduces a local variable which will not be available in the scope of the WebControl. The traditional ASP.NET approach would be to use a Repeater instead of the foreach. It seems to be a slippery slope to use any traditional ASP.NET controls at all, because I suspect you'll soon find yourself needing to combine more and more of them to get the job done.
Plain HTML
Forgoing the abstraction at all, you are left with duplicate presentation code. This is against DRY, but produces very readable code.
It doesnt look like webforms has that much less html to me, it seems more like a lateral move. Using a partial in MVC can make it cleaner but the html markup you needed will still be there, in one place or another. If its mostly the extra html that bothers you, you might take a look at the NHaml view engine or check out haml
the haml website.
I'm by no means a Haml expert but the html does look a lot cleaner...
.top container
.header
%p
The title
%em
(and a small note)
.simpleBox rounded
%p
This is content.
%p
Some more content

Encapsulating User Controls in ASP.NET MVC

Sorry if this is a basic question - I'm having some trouble making the mental transition to ASP.NET MVC from the page framework.
In the page framework, I often use ASCX files to create small, encapsulated chunks of functionality which get inclded in various places throughout a site. If I'm building a page and I need one of these controls - I just add a reference and everything just works.
As far as I can tell, in MVC, the ASCX file is just a partial view. Does this mean that wherever I want to add one of these units of functionality I also have to add some code to the controller's action method to make sure the relevant ViewData is available to the ASCX?
If this is the case, it seems like a bit of a step backwards to me. It means, for example, that I couldn't just 'drop' a control into a master page without having to add code to every controller whose views use that master page!
I suspect I'm missing something - any help would be appreciated.
Thanks,
- Chris
As far as I can tell, in MVC, the ASCX
file is just a partial view. Does this
mean that wherever I want to add one
of these units of functionality I also
have to add some code to the
controller's action method to make
sure the relevant ViewData is
available to the ASCX?
Yes.
However, you can use a RenderAction method in your view instead of RenderPartial, and all of your functionality (including the data being passed to the sub-view) will be encapsulated.
In other words, this will create a little package that incorporates a controller method, view data, and a partial view, which can be called with one line of code from within your main view.
Your question has been answered already, but just for sake of completeness, there's another option you might find attractive sometimes.
Have you seen how "controls" are masked on ASP.NET MVC? They are methods of the "HtmlHelper". If you want a textbox bound to "FirstName", for example, you can do:
<%= Html.Textbox("FirstName") %>
And you have things like that for many standard controls.
What you can do is create your own methods like that. To create your own method, you have to create an extension method on the HtmlHelper class, like this:
public static class HtmlHelperExtensions
{
public static string Bold(this HtmlHelper html, string text)
{
return "<b>" + text + "</b>\n";
}
}
Then in your view, after opening the namespace containing this class definition, you can use it like this:
<%= Html.Bold("This text will be in bold-face!") %>
Well, this is not particularly useful. But you can do very interesting things. One I use quite often is a method that takes an enumeration and created a Drop Down List with the values from this enumeration (ex: enum Gender { Male, Female }, and in the view something like Gender: <%= Html.EnumDropDown(Model.Gender) %>).
Good luck!
You can render a partial view and pass a model object to.
<% Html.RenderPartial("MyPartial", ViewData["SomeObject"]);
In your partial view (.ascx) file, you can then use the "Model" object (assuming you've inherited the proper object in your # Control deceleration) to do whatever you need to with that object.
You can, ofcourse, not pass and Model and just take the partial view's text and place it where you want it.
In your main view (.aspx file), you will need to define the proper object in the ViewData that you're passing to the partial view.
Another method you can do is use:
<% Html.RenderAction("MyAction", "MyController", new { Parameter1="Value1"}) %>
What the previous method does is call a controller Action, take its response, and place it where you called the "RenderAction()" method. Its the equivalent of running a request against a controller action and reading the response, except you place the response in another file.
Google "renderaction and renderpartial" for some more information.

View Models (ViewData), UserControls/Partials and Global variables - best practice?

I'm trying to figure out a good way to have 'global' members (such as CurrentUser, Theme etc.) in all of my partials as well as in my views.
I don't want to have a logic class that can return this data (like BL.CurrentUser) I do think it needs to be a part of the Model in my views So I tried inheriting from BaseViewData with these members. In my controllers, in this way or another (a filter or base method in my BaseController), I create an instance of the inheriting class and pass it as a view data. Everything's perfect till this point, cause then I have my view data available on the main View with the base members. But what about partials?
If I have a simple partial that needs to display a blog post then it looks like this:
<%# Control Language="C#" AutoEventWireup="true" Inherits="ViewUserControl<Post>" %>
and simple code to render this partial in my view (that its model.Posts is IEnumerable<Post>):
<%foreach (Post p in this.Model.Posts) {%>
<%Html.RenderPartial("Post",p); %>
<%}%>
Since the partial's Model isn't BaseViewData, I don't have access to those properties. Hence, I tried to make a class named PostViewData which inherits from BaseViewData, but then my containing views will have a code to actually create the PostViewData in them in order to pass it to the partial:
<%Html.RenderPartial("Post",new PostViewData { Post=p,CurrentUser=Model.CurrentUser,... }); %>
Or I could use a copy constructor
<%Html.RenderPartial("Post",new PostViewData(Model) { Post=p }); %>
I just wonder if there's any other way to implement this before I move on.
Any suggestions?
Thanks!
Have you considered keeping these things in the session and writing a strongly-typed wrapper around the session that will give you access to this information? Then in any view you can simply create a new wrapper class with the ViewPage's (or ViewUserControl's) Session property and use it.

Resources