My site has an ever increasing number of distinct controllers, based around specific entities, which implement the Interface/Repository pattern to populate the various views. Most of these views are bound to single pages. Details, Indexes etc.
This entity data will also be displayed, mostly in summarised/compacted form, on other pages such as a customised home page, and side bars (using layouts and sections).
I'm uncertain as to which approach to use. I have 2 in mind:
1) Import the relevant repositories into the page template/sidebar template:
public HomeController(IMessagesRepository messagesRepository, ICruiseCalendarRepository cruiseCalendarRepository, INewsRepository newsRepository, and so on and so on...)
//
{
this.repoMessages = messagesRepository;
this.repoCruiseCalendar = cruiseCalendarRepository;
this repoNews = newsRepository;
and so on
and on
....
....
}
...and build the page from these or.
2) Access the dynamic data through the controllers by use of Html.RenderAction:
<div class="newsBox">
#{ Html.RenderAction("CustomisedHeadlines", "News");}
</div>
<div class="messageBox">
#{ Html.RenderAction("LatestMessages", "Messages");}
</div>
<div class="calendarBox">
#{ Html.RenderAction("WeekItinerary", "CruiseCalendar");}
</div>
Any suggestions as to the best design practice, would be welcome.
regards, Guy
Use Html.RenderAction is definitely better solution. Its make controller simple and reuseble. You can easily add and remove boxes from view without changes in controller. It is easy to put them in diffrent view. Generally it's more flexible solution.
Related
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...
Im starting MVC & would like your advice about Repositories & View models, specifically combining a few sets of repository data into one VM.
If I have a web page with three distinct sections (columns) that require their own repositories (sorry for the basic nature of this):
UserInfoData - shown in Left col
DiaryData - shown in Middle col
CommentData - Shown in Right col
Im doing the following :-
public ActionResult Index(int id)
{
UserInfoData uid = repository.GetUserInfoData(id);
UserInfoDataVM UserViewModel = Mapper.Map<UserInfoData, UserInfoDataVM>(uid);
//RETRIEVE DiaryData & CommentData using same principle as above
return ??
}
How would I combine these three VM's into something I can return to the view - do I use another VM which accepts these three VM's?
Is there a best practice when dealing with smaller segments of a web page that have their own data?
In my opinion, it depends on your usage scenario.
You could create a View Model that contains the three sub view models, but you would need to ask yourself, am I going to use any of the three sub view models anywhere else? If not, then you could just use one large view model. Else, there is nothing wrong with the 'wrapper' view model that encapsulates smaller view models.
You could keep the three view models as you have defined above and you could use the Html.RenderAction syntax in your view to actually go and fetch each of the sub parts of your page. You would have defined an action on your controller whose responsibility it is to populate each of the small view models and return the view for that model.
<div id="leftCol">
#{Html.RenderAction("LeftCol", "Controller");}
</div>
<div id="midCol">
#{Html.RenderAction("MiddleCol", "Controller");}
</div>
<div id="rightCol">
#{Html.RenderAction("RightCol", "Controller");}
</div>
This would give you the ability to use the sub parts anywhere on your site and you only have to define the action method and partial views once.
The second option is also a personal favorite because you can quickly leverage JQuery to do partial page loading if these sections take a few seconds to populate and render (if this makes sense for your page). Now, your page opens quickly, you have a JS function that goes and fetches these sections, put a little loading graphic over them and are loaded/rendered using an AJAX request. Again, only if this makes sense for you, but using the Partial Views/Render action methods typically give you more flexibility in reuse across the site and AJAX partial page rendering.
I'll explain my point:
The best practice is to create views strongly typed with a Model. You only can stronly type one Model.
If you need two models in a view you can created two views and use Partial Render, but it seems not to be the very best option.
Another approach is to create another type model that encapsulates the other pieces of the model what you need; this make much more sense for me.
Then, my question is, in a complex proyect when a page needs to communicate with all the models and they are not direct realted, developers create a type that encapsulates all the other things?
For non-related parts of your view, you may use Html.Action() to invoke an action that returns a partial view.
This way, the logic of the "area" will be encapsulated in its own action and/or controller.
Update: I don't know if it's really the best practice, but I prefer composition over complex views & view models. Even for related information, I prefer to break it to smaller partial views and child actions. As I see it, it has the following flexibility:
Ability to easily move some of the partial views/child actions to a layout page
Load the partial view asynchronously via AJAX query
Reduced controller action complexity and increased maintainability.
Better support for conditioned rendering
Separation of concerns
In (4) I mean that you can easily do the following without complicating your view model:
<div class="header">
#if (loggedInUser.ShowAds) {
#Html.Action("Header", "Ads")
}
</div>
Answering the question in your comment.
Considering twitter. There's the content pane and the users box on the left.
So here's our TweetsController:
public class TweetsController: Controller {
public ActionResult Index() {
var tweets = ...;
return View(tweets);
}
}
The Tweets/Index view may look like:
#model Tweet[]
<div class="leftPane">
#Html.Action("Index", "Users");
</div>
<div class="mainContent">
#foreach var t in Model {
#t.User - #t.Text
}
</div>
Note that the left pane just calls the Index action in UsersController to display the users list.
Here's how it may look like:
public class UsersController: Controller {
public ActionResult Index() {
var users = ...;
return PartialView(users);
}
}
And here's the partial view (Users/Index):
#model User[]
#foreach var u in Model {
<img src="#u.IconUrl"/> #u.Name
}
So what will actually happen, when the Tweets view will be rendered Html.Action will put the partial view returned by UsersController.Index in the left pane.
Of course you may move this logic into a layout if this behavior is common for a number of pages.
Hope that helps.
Then, my question is, in a complex proyect when a page needs to
communicate with all the models and they are not direct realted,
developers create a type that encapsulates all the other things?
Yes some of them do, and the name for these is ViewModels
I'm stuck! I'm under the impression that the _layout.cshtml file is used for MasterPage-like content. Everything there is rendered on every page. Naturally, I want to write the code for rendering my sidebar menu in that file.
I want to dynamically display a list of Categories from my DB, but I'm having a problem with passing the actual model of categories to Layout.cshtml since it seems no controller actually touches it.
Any suggestions?
Otherwise please tell me how to approach this problem. I've been wracking my brain for the past three days and still no elegant solution.
I need to:
Dynamically fetch a list of Categories from the DB.
Display this list of Categories on every single view. (Hence the use of _layout.cshtml)
Elegantly handle each different categories click.
I'm at my wits end. :P How would you solve this?
_layout.cshtml
#if(isSectionDefined("Categories"))
{
<div id="sidebar">
#RenderSection("Categories", required: false )
</div>
}
index.cshtml
#section Categories {
<ul>
<li>Category One</li>
<li>Category Two</li>
<li>Category Three</li>
</ul>
}
see this : http://weblogs.asp.net/scottgu/archive/2010/12/30/asp-net-mvc-3-layouts-and-sections-with-razor.aspx
Any viewmodel that you pass to your view is automatically available within your master page. If you do not use RenderAction/Action which is the best approach, then you must create the necessary master page data in every action and add it to viewdata - either by having a common base class for your strongly typed viewmodel that contains all master page data or by using the viewdata dictionary.
I would strongly recommend that you go down the html.action approach though. In this way, you have a totally separate controller action for dealing with your list of categories. This action can retrieve the neccesary category data and return the categorylist usercontrol as a partialview and you will not have to worry about polluting all your other actions with this data.
As I see it, ViewData (and its relatives like ViewBag, Model, etc.) is meant for the specific current view. Your _Layout.cshtml is not specific to the current view; and it would be awkward if EVERY controller would have to pass the categories data in addition to whatever else data it needs to pass for the view.
Instead, what I do, is provide a static method in one of my helper classes that retrieves the categories from the DB. I also do some caching there, so that I do not have to hit the DB on every single request. The _Layout.cshtml then simply calls this static method. Simple and elegant.
If you wish, you can bring this out to a partial view, make it a helper method, whatever.
One note of caution though - my custom error view also uses the same _Layout.cshtml, and if the DB goes down, you get an exception trying to display the exception. ASP.NET MVC is smart enough to detect this and abort processing, but you're left with a nondescript default error page. What I did was to place try...catch statements around these dangerous calls, which quietly ignore the exception if the current page is the error view.
I've achieved something similar by having my ViewModels implement an Interface which has members that contain the menu data. In my action method I set that data. Then in my view I check to see if my view-model implements that inteface, pull the menu data out and render the menu (in a partial view actually)
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