views:

947

answers:

3

How would you go about supporting external composable parts in an ASP.NET MVC view?

What do I mean by this? Think either "login box on every page" or "iGoogle". It's stuff that needs to be in certain places that is external to each controller/view.

One approach at this would be adding components in the view like so:

<% foreach (var component in GetComponents()) {%>
 <%= Html.RenderPartial(component.ViewName, component.ViewData)%>
<%} %>

In the example above I'm looking for a good way to have the viewname and viewdata delivered by each component's controller and not the controller of the view they are displayed on. Any totally different solution you can suggest would also be of interest. Filters, WebForms, etc.

Update: I'll try to explain what I'm trying to wrap my head around with an example. I'll pick the login functionality.

In a typical webforms application this could be a user control that retrieves the appropriate data in the load event of the page's life cycle and updates some UI controls. Upon click the page would post back and we can act upon the posted information in the click event in the same user control.

As of my current understanding of the ASP.NET MVC style the controller that first accepts the request would retrieve the appropriate data and pass it to the view which in turn would pass it along to the login partial view. The login view would define a form whose post action is directed at the login action of the login controller. The posted information is used by the login action and we can choose to pass the request along to the original controller using some nifty scheme.

I assume there is a smarter approach than the above that doesn't require I put controller logic in a view/a master page.

+1  A: 

without understanding the question properly, i'd suggest a masterpage approach. ie. have the masterpage render the components and pages derive from the masterpage.

i fear this is not a good answer however. i dont understand this: "..have the viewname and viewdata delivered by each component's controller and not the controller of the view they are displayed on."

cottsak
Thanks for answering. It's a good idea, but I don't think it answers what I'm really after. E.g. How would I prevent controller logic from leaking into the master page using this approach? I've updated the question to better explain myself.
Cristian Libardo
+1  A: 

You could use the master page and use content placeholders for view content pages and view user controls.

The default project template in ASP.NET MVC Framework Beta has a content placeholder for the View Content Page. There is also a View User Control for the login/logout link at the top right corner of the pages.

View Content Page

The view content pages are supposed to be used with the data that comes from the View that you are looking at. I.e. if you're browsing to the Index action in HomeController, the server will render the Home/Index.aspx view content page.

In the master page you need the following line:

<asp:ContentPlaceHolder ID="Holder1" runat="server" />

And in the view content page itself you compose for that place holder with the following:

<asp:Content ID="Content1" ContentPlaceHolderID="Holder1" runat="server">
  <p>This text goes to "Holder1" content place holder and
     I can access the ViewData.</p>
  <p><%= ViewData["Message"] %>
</asp:Content>

Each asp:ContentPlaceHolder corresponds to the view's asp:Content through the id. So if you want several placeholders like this:

<asp:ContentPlaceHolder ID="Holder1" runat="server" />
<asp:ContentPlaceHolder ID="Holder2" runat="server" />

…you can handle them in the view content page like this:

<asp:Content ID="Content1" ContentPlaceHolderID="Holder1" runat="server">
  <p>This text goes to "Holder1" content place holder</p>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Holder2" runat="server">
  <p>This text goes to "Holder2" content place holder</p>
</asp:Content>

View User Controls

View user controls can be used regardless of what view you are looking at. Here are some examples of viewing user controls, are taken from Wekeroad's blog post about View User Controls. To use a view user control:

<%=Html.RenderUserControl(“~/UserControls/UserList.ascx”)%>

If you have the need you can also pass data and anonymous types to it.

<%=Html.RenderUserControl(“~/UserControls/UserList.ascx”,ViewData.Users, new {GroupID=2})%>
Spoike
+5  A: 

There is two ways you can do this:

One: Make a BaseController, that allways gets the data needed for these components, set the ViewData["loginbox"] = loginBoxData, and then you can pass it along like so.

<% foreach (var component in GetComponents()) {%>
        <%= Html.RenderPartial(component.ViewName, ViewData[component.Name])%>
<%} %>

The problem about this method is that you need to have logic to say WHEN all these data needed for the components is going to be fetched, and when not to.

TWO:

There is a MVC Futures DLL that you can download from here.

It you reference that, and remember to add its namespace to your web.config, then you should be able to say: Html.RenderAction("blah") - this method makes the full trip by contacting a controller, initiate the view, and returns the HTML.

Jesper Blad Jensen aka. Deldy
Those futures might just be what I was looking for. I'll check it out. Thanks.
Cristian Libardo