0

When I look at an MVC view which I wrote, it all looks like tag spaghetti to me especially with default color scheme of VS 2008. It's all <% yellow tags %> everywhere. It's really hard to distinguish server and client side code.

In classic ASP it wasn't that bad because usually server-side code blocks weren't as interleaved as lightweight MVC views. They were big chunks thus easily distinguishable. Now there is almost a 1:1 interleave between client-side and server-side code (e.g. one line client, one line server, goes on). I'm on the verge of having an epilepsy attack when I try to understand what a view produces.

If I could at least have a separate background color for server-side code, I think it would help. VS2008 doesn't allow it though.

I removed yellow background color from <%/%> tags, it looks better now, at least fixed the twitch in my eye, but it's still hard to track a view's flow.

I'm also ok with alternative view engines to fix this problem but I don't know if there is any that provides some of WebForms' luxuries:

  • Syntax highlighting for server-side code
  • Intellisense
  • Compiled

I looked at view engines listed in SO, specifically Spark, but I didn't like how it mingles with HTML code. I think it makes the mentioned problem worse.

Here is a sample code that I don't like:

<%@ Page Title="" Language="C#" 
    MasterPageFile="~/Views/Shared/site.master" 
    Inherits="System.Web.Mvc.ViewPage<SomeModel>" %>
<%@ Import Namespace="SomeHelpers" %>
<asp:Content ID="Content1" ContentPlaceHolderID="body" runat="server">
<h1><%= Html.Encode(Model.Title) %></h1><br /><br />
<% if (Model.Found) { %>
    <% foreach (var item in Model.List) { %>
    <div class="item">
       <%= item.ProductID %> - <%= Html.SomeHelper(item.Description, 32000) %>
       <div class="itemsub">(<%= Html.SomeOtherHelper(item.Customer) %>, 
       <%= Tools.AnotherHelper(item.OrderDate, item.ShipDate) %>)</div>
    </div>
    <% } %>
<% } else { %>
    Item Not Found <br />
    <% if (Model.Items.Count > 0) { %>
       Some Text Here<br />
       <ul>
       <% foreach(var i in Model.Items) { %>
           <li><%= Html.SomeHelper(i) %></li>
       <% } %>
       </ul>
    <% } %>          
<% } %>
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="title" runat="server">
<%= Html.Encode(Model.Title) %> - Badass Web Site
</asp:Content>

After pasting the code I noticed that SO does a better job in highlighting server/client-side code properly :) I don't think this kind of highlighting is possible in VS2008 though (you'd have to change C# highlighting completely which I wouldn't prefer).

Community
  • 1
  • 1
Sedat Kapanoglu
  • 46,641
  • 25
  • 114
  • 148

2 Answers2

3

1. Partial Views are Your Friend

Use RenderPartial and separate partial views for any item that falls within a loop. This also can help with live updating with ajax because you can get the rendered HTML for a single item very easily without doing a page refresh.

 <% foreach (var item in Model.List) { %>
<div class="item">
   <%= item.ProductID %> - <%= Html.SomeHelper(item.Description, 32000) %>
   <div class="itemsub">(<%= Html.SomeOtherHelper(item.Customer) %>, 
   <%= Tools.AnotherHelper(item.OrderDate, item.ShipDate) %>)</div>
</div>
<% } %>

To:

<% foreach (var item in Model.List) 
       Html.RenderPartial("itemTemplate", item ); %>

2. Make the ViewModel pull its Weight

Do a lot more string manipulation and logic in your view models instead of loading up with single use Html helpers or similar. Maybe put HtmlEncoding in your ViewModels property setters? Or use methods like GetBoldedFirstName() in your ViewModel

Now this does kind of muddle the separation between C# and HTML markup BUT your Html markup will thank you for being cleaner. I personally don't like having 100s of single use Helpers floating around and think this technique makes the markup read better.

<div class="item">
   <%= item.ProductID %> - <%= Html.SomeHelper(item.Description, 32000) %>
   <div class="itemsub">(<%= Html.SomeOtherHelper(item.Customer) %>, 
   <%= Tools.AnotherHelper(item.OrderDate, item.ShipDate) %>)</div>
</div>

To:

<div class="item">
   <%= item.ProductID %> - <%= item.FormattedDescription(3200) %>
   <div class="itemsub">
       (<%= item.GetCustomerName() %>, 
        <%= item.GetPrettyOrderStatusString() )
   </div>
</div>

3. Use an Empty View

I try and keep my markup as stupid as possible and prefer to bake as much logic into the actual view model or action method I can.

For "empty" pages that I try to do is make a shared view for all my empty grids just like you would make a single 404 page for a website. Your controller should know that you have nothing to show and return the appropriate View.

You'll save is a lot of nesting and Count>0/empty/null checking in your markup.


Rob Connery has an excellent article on this topic as well: http://blog.wekeroad.com/blog/asp-net-mvc-avoiding-tag-soup/

John Farrell
  • 24,673
  • 10
  • 77
  • 110
  • Thanks! About your points: 1) Hadn't looked at RenderPartial, will check it out. I think there is overhead of maintaining too many files there at that granularity level. 2) Moving HTML to C# code just moves the problem there, doesn't fix it. Rob's article uses a similar approach. 3) Use a different view for not found case is a good advice, thanks! I hope answers keep coming up. It looks like I'm not the only one here :) – Sedat Kapanoglu Dec 02 '09 at 23:26
  • Revisiting your answer @jfar, your points make much more sense today. I apparently misunderstood your second recommendation which actualy removes too much overhead from the view and makes it a lot more readable. Thanks again. – Sedat Kapanoglu May 08 '13 at 11:11
1

Well, Spark has everything you mentioned - syntax highlighting, Intellisense (works 80% though), views compilation and precompilation. And you're not forced to "mingle" HTML code, you can still use manual C# code, and even existing WebForms engine markup!

So either of the following will work in Spark

<% foreach (var product in Model) { %>
  <li><%= product.Name %></li>
<% } %>

# foreach (var product in Model) {
  <li>${product.Name}</li>
# }

<li each="var product in Model">${product.Name}</li>

To me the latter is a clear winner. But at least you have choice.

queen3
  • 15,333
  • 8
  • 64
  • 119