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).
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/
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.
Related
In my quest to keep my application views as DRY as possible I've encountered a little snag. My appliation.html.erb incorporates a static sidebar menu. Each of my main controllers incorporates a secondary sidebar menu (essentially a submenu). I can take the code that renders the menu out of application.html.erb and put it in each of my views and change the secondary sidebar there, but this produces a lot repetition in my views.
I saw this SO post and looked at this page, but I was unable to get either idea to work. I was thinking that I could put something like:
<% provide(:submenu, 'layouts/sidebars/sidebar_customers_contacts') %>
at the top of each view and use that to render the associated partial by doing
<% content_for(:submenu) do %>
<%= render :partial => :submenu %>
<% end %>
from the application.html.erb but of course that didn't work.
This is my current application.html.erb:
<div class="side">
<%= render 'layouts/sidebar' %>
<%= render 'layouts/sidebars/sidebar_dashboard' %><!-- this needs to load a sidebar based on the controller that calls it. Each view of the controller will get the same sidebar. -->
</div>
<div class="main-content">
<%= yield %>
</div>
I feel like I'm making this more difficult than it really is. Is there a simple way to do this?
Rails provides a helper called controller_name which you can read more about here.
Assuming you adhere to your own naming conventions, this should work as-is. If you decide some controllers don't get a sidebar, you may need to throw in some conditionals...
application.html.erb
<div class="side">
<%= render "layouts/sidebar" %>
<%= render "layouts/sidebars/#{ controller_name }" %>
</div>
<div class="main-content">
<%= yield %>
</div>
EDIT
Sorry, my mistake was using single quotes instead of double-quotes. You cannot use #{string interpolation} within single quotes. Source
I'm facing a problem of cleaning up my application in Ruby on Rails. What I have is a pretty standard 3-panel, header and footer layout where different parts of the screen contain different functionality.
By that I mean for example that the header contains (among others) a select that allows one to select parts of the application and a context-dependent menu. The main content area contains obviously the most interactive stuff whereas side panels contain quick-links with stuff like shopping-cart preview, list of potentially attractive products for the customer, a selector to narrow down the list of options...
I was wondering how do I go about simplifying the design. Right now I have the stuff that provides data for the "common" stuff (as opposed to direct content that's placed in the center) called from all the actions (with a filter) but that doesn't feel right for me. I've read that "components" are also not the way to go for obvious performance reasons.
Is there something that's more like component-oriented (other frameworks do have that kind of stuff - Grails: <ui:include ../>, ASP.NET MVC: <% Html.RenderAction() %>)?
Best regards, Matthias.
You can use content_for for managing different location of a layout.
layout.html.erb
<body>
<div id="header">
</div>
<div id="content">
<div id="main">
<%= yield %>
</div>
<div id="side_bar">
<%= yield :side_bar %>
</div>
</div>
<div id="footer">
</div>
</body>
In your views use content_for to assign content to various sections of the layout:
<% content_for :side_bar do %>
<div>
<h1> Popular Posts </h1>
</div>
<% end %>
<div>
<h1> All Posts </h1>
<% #posts.each do |post| %>
<h2> <%= post.name %> </h2>
<% end %>
</div>
Reference:
Railscast
Edit 1
You can use partials to create reusable screen sections.
views/shared/_shopping_cart.html.erb
<div>
display the shopping cart details
<div>
views/posts/index.html.erb
<% content_for :side_bar do %>
<%= render :partial => 'shared/shopping_cart' %>
<% end %>
Essentially you can use:
Filters to set data
content_for to set layout
partials to display data
Combination of these three should allow you to componentize your solution.
layout
In app/views/layouts, a global layout application.html.erb defines the outer most layout (e.g. your 3-panel design).
The application layout can be overrided by a controller layout. For example, if you have a Tasks controller, you can add a tasks.html.erb in the app/views/layouts folder. The tasks.html.erb will be your default layout for any Tasks views (index, add, edit, etc.)
In application.html.erb or tasks.html.erb, you may add two partials, e.g. _header.html.erb, _side.html.erb. Those two partial will serve as the default panels for the views.
A better tutorial is at http://guides.rubyonrails.org/layouts_and_rendering.html
In the new version of MVC we have been given display and editor templates. I was wondering how to make the best possible use of these. I was at first thinking in the line of creating one for a collection and one for an item in the case I want to display either as a list or single but it does not really make sense to me. I'll show you what I mean and please comment if this is wrong. I have trouble figuring it out myself.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<DisplayPhoneModel>>" %>
<% foreach (var item in Model){%>
<%= Html.DisplayFor(x => item) %>
<%}%>
So in my tiny reality that would for every item in the model search for a display template for DisplayPhoneModel. That display template could then simply look like.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DisplayPhoneModel>" %>
<%= Html.LabelFor(x => x.PhoneType) %> <%= Html.LabelFor(x => x.PhoneNumber) %>
So, question is if there is a better way to handle this? Am I at all on the right path?
No need to foreach manually:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<DisplayPhoneModel>>" %>
<%= Html.DisplayForModel() %>
And the display template:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DisplayPhoneModel>" %>
<%= Html.LabelFor(x => x.PhoneType) %>
<%= Html.LabelFor(x => x.PhoneNumber) %>
I'm new to Ruby and Rails and I have a simple controller that shows an item from the database in a default view. When it is displaying in HTML it is outputting <p> tags along with the text content. Is there a way to prevent this from happening? I suppose if there isn't, is there at least a way to set the default css class for the same output in a statement such as this:
<% #Items.each do |i| %>
<%= i.itemname %>
<div class="menu_body">
Link-1
</div>
<% end %>
So the problem is with the <%= i.itemname %> part. Is there a way to stop it from wrapping it in its own <p> tags? Or set the css class for the output?
Thanks!
You need to enclose it with the HTML tag of your choice. Also if required you can escape bad code by using <%=h i.itemname %> Example:
<% #Items.each do |i| %>
<div><%=h i.itemname %></div>
<div class="menu_body">
Link-1
</div>
<% end %>
Edit: Ryan Bigg is right. Rails doesn't output a <p> tag. Sorry for the wrong info.
You canchange the public/stylesheets/scaffold.css if you want.
Or if you want to change it for a single page say items/index.html.erb
<style>
p{
/* your style here *?
}
</style>
is there a way in ASP.NET MVC(2) to do something like that:
<table>
<tr>
<% Model.Pager.TemplateNextPage = { %>
<li><a href={link-next-page}>Next</a></li>
<% } %>
<% Model.Pager.TemplatePageNumber = { %>
<li><a href={link-page-number}>{number}</a></li>
<% } %>
<% Model.Pager.TemplatePageNumberActive = { %>
<li>{number}</li>
<% } %>
</tr>
</table>
the general idea is to enable designers to specify templates.
Not like that no. The code betweeen "%>" and "<%" e.g.
<% Model.Pager.TemplateNextPage = { %>
<li><a href={link-next-page}>Next</a></li>
<% } %>
... will translate to ...
Model.Pager.TemplateNextPage = {;
Response.Write("<li><a href={link-next-page}>Next</a></li>");
};
when compiled (ignoring the whitespace in the example). So it does not really work.
What you can do however, is use Asp.Net WebForms controls and create a custom one. It will work within Asp.Net MVC page. Not all features work (e.g. related to form postback), but enough to do what you want.
E.g. a table control is like this:
<asp:Table runat="server">
<asp:TableFooterRow>
<asp:TableCell>foo</asp:TableCell>
<asp:TableCell>faa</asp:TableCell>
</asp:TableFooterRow>
</asp:Table>
You could extend that - or create a control from scratch allowing you to create your own TemplateNextPage etc.
...but this is not Asp.Net MVC way - it is the old Asp.Net WebForms way. It just works within Asp.Net MVC pages.
The other option you have is to create your own template engine, but that's quite a lot of work and I assume the intellisense part is very important to you so it would screw that as well.