Syntax for ASP.Net MVC Phil Haack's Repeater syntax using Razor (MVC 3)? - syntax

I have recently started using ASP.Net MVC 3 RC 2 and have attempted to migrate an existing website in MVC 2 across using the Razor syntax. In the MVC 2 application I am using the code base repeater that Phil Haack kindly provided in the following:
Phil Haack's Code Based Repeater
My question is around the syntax for Razor. I dont understand how the template in the following block can be rewritten in Razor and cannot find any documentation to help out (early days for documentation or my simplicity...):
<% Html.Repeater<ObjectToUse>(Model, "", "alt", (item, css) =>
{ %>
<tr class="<%= item.Enabled ? css : "disabled" %>">
<td><%= item.Name%></td>
<td><%= item.Description%></td>
<td><%= Html.RouteLink("Edit", item.ObjectToUseRouteValues("Edit"))%></td>
<td></td>
<td><%= Html.RouteLink("Select", item.ObjectToUseRouteValues())%></td>
</tr>
<% }); %>
The problem comes when applying the template between the braces (the tr's). I have attempted using the WebGrid control, however it doesnt provide the functionality I require for setting a "disabled" row (I think).

I wrote #helper version.
#helper do not use Generic method.
#helper ForEach(IEnumerable<int> items, Func<object, HelperResult> template){
foreach(var item in items){
Write(template(item));
}
}
<div>
<ul>
#ForEach(Enumerable.Range(1,5),
#<li>#item</li>
)
</ul>
</div>
hope this code.

Actually, now that I think about it some more I don't think you can use Action parameters like that in Razor. I recall running into this before.
Updated
With answer from Andrew Nurse:
"Unfortunately this is by design in the current parser, though I should note that we'd like to improve upon it. The issue is that markup is only valid at the start of a statement (which technically is where you've put it), but our C# "parser" is not intelligent enough to detect lambdas at the moment."
Though that might be out dated :)
#Html.Repeater(Model, "row", "row-alt",
#<tr class="#item.ClassType : "disabled"">
<td>#item.Name</td>
<td>#item.Description</td>
<td>#Html.RouteLink("Edit", item.ObjectToUseRouteValues("Edit"))</td>
<td></td>
<td>#Html.RouteLink("Select", item.ObjectToUseRouteValues())</td>
</tr>
)
public static IHtmlString Repeater<T>(this HtmlHelper html, IEnumerable<T> items,
string className, string classNameAlt, Func<T, HelperResult> render) {
if (items == null)
return new HtmlString("");
int i = 0;
StringBuilder sb = new StringBuilder();
foreach (var item in items) {
item.ClassType = item.Enabled ? (i++ % 2 == 0 ? className : classNameAlt) : "disabled";
sb.Append(render(item).ToHtmlString());
}
return new HtmlString(sb.ToString());
}
}

Related

MVCContrib Grid MVC 3 Razor .RowStart

Having trouble with the examples for the .RowStart method.
Comparing the 2 syntaxes: http://www.jeremyskinner.co.uk/2009/03/01/mvccontrib-grid-part-5-the-action-syntax/
In this
.RowStart(row => string.Format("<tr{0}>", row.IsAlternate ? "style=\"background-color:#CCDDCC\"" : ""))
row.IsAlternate throws an error as row isnt the GridRow, its actually your model (well the row's data item).
The second syntax (ActionSyntax) :
.RowStart((p,row) => {
if (row.IsAlternate) { %>
<tr style="background-color:#CCDDCC">
<% } else { %>
<tr>
<% }
}).Render(); %>
doesnt seem to translate to Razor
.RowStart((x, row) => string.Format("<tr class='{0}'>", row.IsAlternate ? "grid-row" : "grid-row-alt"))
Passes ok, but doesn't emit any row changes.
Any had this working?
I've just noticed some convention stuff that Html.Grid is putting in for you...
Given the Following
Html.Grid(Model.Results).Attributes(#class => "grid")
results in a table with class = "grid",
even rows with a class = "gridrow",
and odd rows with a class = "gridrow_alternate"
Not sure if this will help but one thing I've been doing lately is using:
.RowAttributes(x => new Dictionary<string, object> { { "class", x.value == myValue ? "highlight" : "" } })
This allows me to do a lot with css values for the attributes. then for supporting "Zebra striping" I use pure css (browser compatibility could be an issue here, but it's graceful just doesn't render on old browsers) looks something like
tr:nth-child(odd) {
background-color: #eee;}
Gives u great control over the table. More info on the selectors Sitepoint child selectors
Other wise you could try the google groups for mvccontib Jeremy is usually sharp of the mark to help out.
Hope this helped.

Razor syntax to declare action that renders html

I'm trying to create an action that renders some html in the razor view engine. This was pretty easy in the webforms engine but I'm having some issues with razor. Here is the simplest form of what I'm trying to accomplish using the webforms engine:
<% var myAction = new Action<HtmlHelper<int>>((helper) => { %>
<div>
<%= helper.ViewData.Model %>
</div>
<%}); %>
The closest I've come in the razor view engine is:
#{var myAction = new Action<HtmlHelper<int>>((help) =>
{
#<div>
#help.ViewData.Model
</div>;
});
}
This gives a "CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement" error.
Any help would be appreciated. Thanks.
#{
Func<dynamic, object> myAction =
#<div>
#item.ProductName
</div>;
}
#myAction(Model)
You may also checkout the following blog post.
UPDATE:
You may also do this:
#{
Func<HtmlHelper<int>, object> myAction = #<div>#item.ViewData.Model</div>;
}
or:
#{
Func<dynamic, object> myAction = #<div>#item.ViewData.Model</div>;
}
and to invoke:
#myAction(someInstanceOfTheRequiredHelper)

Does it 'break' MVC to have information about a model in a view?

I'm new to MVC and I have a question concerning my view.
I have a strongly typed view:
#model CellularAutomata.Models.D1CellularAutomata
#{
ViewBag.Title = "View";
}
<h2>View</h2>
<table>
#foreach (CellularAutomata.Models.Grid grid in Model.GridHistory){
<tr>
#foreach (CellularAutomata.Models.Cell cell in grid.Cells[0]){
if (cell.State == CellularAutomata.Models.State.On){
<td>X</td>
}
if (cell.State == CellularAutomata.Models.State.Off){
<td>O</td>
}
}
</tr>
}
</table>
Does it break the rules of MVC to reference parts of the model in my view, such as
(CellularAutomata.Models.Cell cell in grid.Cells[0])
or
(cell.State == CellularAutomata.Models.State.On)
If this is incorrect, what is the best way to go about fixing it?
Not at all, since your view is strongly typed against your model. If your view was model agnostic then it would be a problem, but as this is a view for D1CellularAutomata it is appropriate to have D1CellularAutomata specific references in your view.

HtmlHelper in ASP.NET MVC3 that uses Action<T> as template: Razor syntax?

Let me preface this by saying that perhaps there's a better way to do this, and Razor is lighting the way. In any case, I have an HTML helper that acts as a repeater of sorts, but after an arbitrary number of repeats, it inserts an alternate template. Most obvious use? Tables that start a new row after x cells. The helper looks like this:
public static void SeriesSplitter<T>(this System.Web.Mvc.HtmlHelper htmlHelper, IEnumerable<T> items, int itemsBeforeSplit, Action<T> template, Action seriesSplitter)
{
if (items == null)
return;
var i = 0;
foreach (var item in items)
{
if (i != 0 && i % itemsBeforeSplit == 0)
seriesSplitter();
template(item);
i++;
}
}
And in a Webforms view, the usage looks like this:
<table>
<tr>
<% Html.SeriesSplitter(Model.Photos, 4, photo => { %>
<td><img src="<%=ResolveUrl("~/Thumbnail.ashx?id=" + photo.ID)%>" alt="<%=Html.Encode(photo.Title)%>" /></td>
<% }, () => { %></tr><tr><% }); %>
</tr>
</table>
In this case, you'd have a table that renders four cells, then starts a new row by using the alternate template (the row start and end tags). The problem is that I can't find a way to make this work in Razor. Using a lambda inside of a Razor view seems like a pretty weird construct.
What would you do?
Phil actually had a good post that solves the problem here:
http://haacked.com/archive/2011/02/27/templated-razor-delegates.aspx
This is a job for Func<object, HelperResult> (though I'm doing this from memory so it might need tweaking):
public static void SeriesSplitter<T>(this System.Web.Mvc.HtmlHelper htmlHelper,
IEnumerable<T> items,
int itemsBeforeSplit,
Func<object, HelperResult> template,
Func<object, HelperResult> seriesSplitter) {
//pretty much same code here as before
}
Then you would call it like so
#Html.SeriesSplitter(Model.Items, 3, #<td>item.Id</td>, #:</td><td>
)
Just note that because of how Razor does tag balancing during parsing you need the new line after #:</td><td>

Why can't I use the ASP.NET MVC 3 Razor engine (with the new #-syntax) in a UserControl?

I am creating a UserControl that will read data from an XML file and populate a ListBox. I've open up the .ascx file and would like to use #foreach (etc..) but this isn't valid?
Can someone explain why this isn't possible? Or maybe confirm if UserControls are deprecated in MVC 3?
.ascx is WebForms. # is Razor, meaning that if you want to write #foreach(...) you need a Razor template: Foo.cshtml:
#model IEnumerable<AppName.Models.Foo>
#foreach (var item in Model)
{
...
}
And if you want WebForms user control Foo.ascx:
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<AppName.Models.Foo>>"
%>
<% foreach(var item in Model) { %>
...
<% } %>

Resources