views:

407

answers:

4

Hi,

Quoting from the NerdDinner ASP.NET MVC sample application

    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%
    if (Request.IsAuthenticated) {
%>
        Welcome <b><%= Html.Encode(Page.User.Identity.Name) %></b>!
        [ <%= Html.ActionLink("Log Off", "LogOff", "Account") %> ]
<%
    }
    else {
%> 
        [ <%= Html.ActionLink("Log On", "LogOn", "Account") %> ]
<%
    }
%>

This is from the partial view usercontrol named LoginStatus.ascx. As you can see there is condition present which alters the 'entire' output of the view. Is this the correct way. Would it have been better if there was controller evaluating this condition and then rendering the appropriate partial view?

And regardless of your answer to the previous question, How can I take the latter approach in ASP.NET MVC i.e. can a parent view call a controller (instead of doing a RenderPartial of the UserControl) and let it decide which partial view to render?

+1  A: 

I think that if the view is going to change accordingly to some condition it is responsibility of the view to enforce this. But if the condition is changing not appearance (i.e. "the negative numbers shall be red") but behaviour (i.e. "if the user is logged in s/he must see the LOGOUT button instead of the LOGIN button") then it's controller's to decide. You might introduce a "renderer" level between controller and page.

Manrico Corazzi
I tend to agree with your reasoning but not your example. The button is a view's responsibility. Would you then also let a controller decided what view to show if it is print/non print? I would let a css file do that.
borisCallens
Manrico Corazzi
+3  A: 

How about this approach:

Solution 1:

Create an extension method on HtmlHelper which will render whether "WelcomeMessage.Anonymous.aspx" or "WelcomeMessage.Authenticated.aspx" view based on the request.

<%= Html.LoginStatus() =>

And put these views under /Views/Shared

/Views/Shared/LoginStatus.Anonymous.ascx
/Views/Shared/LoginStatus.Authenticated.ascx

Solution 2:

Just replace if / else statements with ASP.NET's LoginView control in your LoginStatus.ascx

<asp:LoginView Runat="Server">
    <LoggiedInTemplate>
        Welcome, <%= Html.Encode(Model.UserName) %>!
        <button>Sign Out</button>
    </LoggedInTemplate>
    <AnonymousTemplate>
        <button>Sign In</button> | <button>Join Now!</button>
    </AnonymousTemplate>
</asp:LoginView>

See also:

Koistya Navin
@Koistya Navin .NET: Good for a hack but the question still stands. Can I not take the second approach I described with ASP.NET MVC?
Ali Kazmi
A: 

I think what you are doing is quite allright. Such little things as changes in displaying could and should be undertaken by views.

For example I have a main menu rendered by a separate ascx. There are lots of such little checks inside in order to decide what text to show and which styles to apply to list elements.

If there are big decisions like thinking of what view to render based on some user action then the controller questions the business logic and decides what view to return of where to redirect. But if it is a quite stable UI element where only text and color options are slightly changed then put the necessary logic into it.

You could also pass a separate model to your ascx with several flags defining what and how should be displayed. Then the actual logic to set those flags will be somewhere else on the business logic level and all your view will do is just look at those flags and render accordingly.

Don't worry, you're doing it right.

User
@Mastermind: Well I quoted this example for brevity, but this question can easily map to a larger scenario. Even here the condition is determining not only the appearance of the output but the functionality too i.e. should the user be allowed to login or logoff.
Ali Kazmi
If these two views only differ cosmetically like having different colors and some small changes like authenticated/notauthenticated text then it's ok to have it in one control. If these views are really different like user view and administrator view then it's two different views now and they should be separately implemented.
User
A: 

Instead of deciding if the user is authenticated in the view, you can do it in the controller, like so:

public ActionResult ShowAPage()
{
   if(!HttpContext.User.Identity.IsAuthenticated) 
   {
      return RedirectToRoute("ShowLoginPage")
   }
   return View();
}

Then you can redirect to a login page rather than having this logic in a view, which is not really a separation of concerns.

A really cool way of doing this is to use a different master page for authenticated users than for not authenticated users. I take it you are wanting to always show either a log on, or log off link, this way you could use an ActionFilter to change the master page depending on the authentication of the user. Then your logged on users can get things like navigation bars that you would maybe want to hide from outsiders, without you having to decide this in display logic.

Here's one way of doing it without using an action filter, but you can do all kinds of ways, one good one is to create a custom controller that inherits from controller and overrides the View methods to select the appropriate master page.

public ActionResult ShowAPage()
{
   if(!HttpContext.User.Identity.IsAuthenticated) 
   {
      return View("ShowAPageView", "LoggedInMasterPageName");
   }
   return View("ShowAPageView", "LoggedOutMasterPageName");    
}

Hope this helps.

Mark Dickinson