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) %>
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 am trying to render 2 partial views using same loginmodel:
<div id="divLoginPopupContent" class="popupContent">
<div id="divLPBox"><% Html.RenderPartial("LoginPopup", ViewData["LoginModel"]); %></div>
<div id="divFBBox"><% Html.RenderPartial("RetrievePassword", ViewData["LoginModel"]); %></div>
</div>
and it is giving me an error
and ViewData["LoginModel"] = new LoginModel();
can you tell me whats the problem?
You are likely having more than one <%# Control ... > directives in one of your partials LoginPopup.ascx or RetrievePassword.ascx. Make sure there is only one control directive.
try strongly typing your view
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<LoginModel>" %>
then pass in the model
<div><%Html.RenderPartial("LoginPopup",Model) %></div>
<div><%Html.RenderPartial("RetrievePassword",Model) %></div>
I have this on my view and this works well, persisting the country selected:
<%: Html.DropDownListFor(model => model.CountryId, Model.CountryList ,"Select Country") %>
However when I create an editor template for this, the country selected is not persisted
So, I change my current view to this:
<%: Html.EditorFor(model => model.CountryId,new { countries = Model.CountryList}) %>
Then I create my editor template like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.Int64?>" %>
<%= Html.DropDownList(
String.Empty /* */,
(SelectList)ViewData["countries"],
Model
)
%>
I would try something like this:
Code in parent view:
<% Html.RenderPartial("YourPartialView", Model) %>
Code in editor view:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ModelFromParent>" %>
<%= Html.DropDownList(
String.Empty /* */,
new SelectList(Model.Countries,"Id", "Title"),
"Select Country"
)
%>
I didn't compile this. But you get the general idea.
I have another simple question for all you mvc gurus.
I have a partial view with the following definition.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IList<whoozit.Models.PictureModel>>" %>
How do I access the data that has been assigned to the view?
You could do something like:
<% foreach (whoozit.Models.PictureModel picture in Model)
{ %>
<%: picture.property1 %><br />
<%: picture.property2 %><br />
etc...
<% } %>
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.