How to pass in a lambda to a Razor helper method? - c#

I have a razor helper method that needs to take in a Func<> that will return some HTML content to print out. This is what I originally had:
#helper node(string title, Func<HelperResult> descriptions)
{
....
<div>#descriptions()</div>
....
}
#node("title",
new Func<HelperResult>(() =>
{
return new HelperResult(
#<text>
<span>"desc1"</span>
<span>"desc2"</span>
</text>);
}))
Unfortunately with this my text never gets printed out. No error either.
So I learned about inline helpers, and changed the calling method to this:
#node("title",
#<text>
<span>"desc1"</span>
<span>"desc2"</span>
</text>)
However now I get a compilation error saying
"Delegate 'System.Func' does not
take 1 arguments".
But I'm not passing in any arguments.
So if I change it to Func<object,HelperResult> and then call it using #descriptions(null) I get the following error:
"Cannot use a lambda expression as an argument to a dynamically
dispatched operation without first casting it to a delegate or
expression tree type"
I'm sure I have something wrong somewhere, but I'm not sure what it is.
Edit: I think I may have solved that problem but it introduces some other issues.
What I did was to cast the lambda before passing into a dynamic method. I guess that's what the error was trying to say:
#node("title",
((Func<dynamic, HelperResult>)(#<text>
<span>"desc1"</span>
<span>"desc2"</span>
</text>))
That works and it prints out the span tags correctly. Unfortunately I have to pass in a useless parameter when calling this Func.
Now the issue I have is that my real function does a bit more than just write some spans. It's more like this:
#node("title",
((Func<dynamic, HelperResult>)(#<text>
<span>#Helpers.Format(resource.Description,"item")</span>
</text>))
Where #Helpers.Format is another helper and resource is a (dynamic) variable from the page model.
Of course now the code runs but nothing is printed out (inside the <span> tag). I put a breakpoint inside my Format helper function, and it hits it and all the parameters are correctly set, so I'm not sure why it wouldn't output correctly. Similarly if I just change it to
resource.Description
then nothing still gets output.
Since it works well outside of this context, I wonder does Razor's inline helpers not capture the outer variables?

Actually HelperResult is something Microsoft would rather you didn't use, as evidenced by documentation:
public class HelperResult : IHtmlString in namespace
System.Web.WebPages
Summary: This type/member supports the .NET Framework infrastructure
and is not intended to be used directly from your code.
A possible solution to your problem might be to wrap your description function in another helper and then pass that helper as a method group to your node helper, like this:
#helper Node(string title, Func<HelperResult> descriptions)
{
<div>#descriptions()</div>
}
#helper Description() {
<span>desc1</span>
<span>desc2</span>
}
#Node("title", Description)
In any case, your first idea shouldn't work because a parameter of type Func is in fact equal to a parameterless function, in which case you need to write the lambda expression like this:
myFunction( () => doSomething)
So your function call would have been:
#node("title", () =>
#<text>
<span>"desc1"</span>
<span>"desc2"</span>
</text>)
Since the future of these helpers is a bit dubious though, I would consider switching to either HtmlHelpers for small snippets of html or Partials for larger chunks.

#Test(new Func<object, HelperResult>[]{#<text>hello</text>})
#Test(new Func<object, HelperResult>[]{#<text>hello</text>,#<text>world</text>})
#helper Test(params Func<object, HelperResult>[] results)
{
foreach (var result in results)
{
#result(null);
}
}

Related

What does DisplayNameFor do?

I'm learning .NET Core with Razor pages, using one of the official tutorials here, and I'm having trouble with this code:
#Html.DisplayNameFor(model => model.Movie[0].Title)
The tutorial says:
The DisplayNameExtensions.DisplayNameFor HTML Helper inspects the Title property referenced in the lambda expression to determine the display name. The lambda expression is inspected rather than evaluated. That means there is no access violation when model, model.Movie, or model.Movie[0] is null or empty. When the lambda expression is evaluated, for example, with #Html.DisplayFor(modelItem => item.Title), the model's property values are evaluated.
Which I can't make heads or tails of. What does inspect mean here? Does it mean the lambda function runs in an try/catch, to prevent the access violation errors that the docs speak of? What does it mean exactly?
And in what important way is the second example (with DisplayFor) different? It uses DisplayFor instead of DisplayName, and another change is that it uses ModelItem instead of model. I don't know where it would get ModelItem from, model is made available by #model (...) at the op of the razor page but how ModelItem gets here is not clear to me.
The docs for DisplayNameForare here, but the tutorial links to the non-core docs here, both of which are too terse for me to make much sense of.
DisplayNameFor will look for the Name property of the Display property attribute and print it to your razor page (or the property name itself, if it can't find it). So, if your model is
class Foo
{
[Display(Name="My name")]
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
It will display My name for Prop1 and Prop2 for Prop2.
The part about it being inspected rather than evaluated, means that it will look at your model definition, it will not try to get a value, so if the model value is null, it will not throw.
On the other hand, DisplayFor will print the value of the selected property, applying any format described by DisplayFormatAttribute, so the model is evaluated and cannot be null.
Typically you will use DisplayNameFor to build the table headers and DisplayFor to build the table data.
What does inspect mean here?
DisplayNameFor is used to get the name of the property and not the value of it, so you will get Title and not Pets 2. In the second case the lambda expression would be evaluated - the first element of the movie list would be retrieved and then its title, that is not happening here.
The difference between evaluating and inspecting is similar to that between reading a book vs turning it around to find out who published it.
2)
And in what important way is the second example (with DisplayFor) different?
DisplayFor is used to format the data itself (Pets 2 and not the Title). In this case there is not much formatting involved because it is a string, but if it was a number for example you could specify how many digits you want to see ect.
3)
I don't know where it would get ModelItem from
ModelItem it is just the name of the variable to pass into the function. modelItem => item.Title is an equivalent of MyFunction(Movie modelItem){return item.Title;} modelItem can be called anything else, particularly as they don't even use it in the function itself and use item directly.

What's the syntax for creating HandleBars helpers server side?

Using Handlebars.Net, I'd like to create a HandlebarsHelper that will replace carriage returns and newlines with <br> tags. It should look something like this:
string pattern = #"/(\r\n|\n|\r)/gm";
string replacement = "<br>";
Regex rgx = new Regex(pattern);
Handlebars.RegisterHelper("link_to", (string text) =>
{
text = rgx.Replace(text, replacement);
});
The compiler (or resharper) is telling me that it can't tell if I'm trying to use HandlebarsBlockHelper or HandlebarsHelper, and I'm missing arguments in either case.
What's the difference between the two?
I can't seem to find much documentation for any of this. Is there documentation for the above two mentioned objects as well as HelperOptions, and how to use TextWriter, the dymanic context and the argument object list?
It ended up looking like this with a little help from the C# Regex class:
var newlineRegx = new Regex("(\\r\\n|\\n|\\r)",RegexOptions.Multiline);
Handlebars.RegisterHelper("handleNewLines", (output, context, arguments) =>
{
var str = newlineRegx.Replace((string)arguments[0], "<br>");
output.Write(str);
});
To answer my questions:
HandleBarsBlockHelper provides a mechanism for invoking a helper with a block of the template. Block helpers can then invoke that block zero or more times with any context it chooses. Check out the description for Helpers for more info (at the bottom you'll see a button labeled "Learn More: Block Helpers").
TextWriter.Write is how you output your transformed text.
context will essentially be the JSON object you passed into the delegate you created with Handlebars.Compile().
The argument object lists contains the arguments that appear along side the helper you're defining, which you'll use in your HTML template
To better understand the argument object list, it'll help to see how I used this helper in my HTML template:
<div>
<p>{{{handleNewLines StringVariable}}}</p>
</div>
Where "StringVariable" is a member of the JSON object I passed into the delegate I created with Handlebars.Compile()

razor template delegate, how the inside works?

I'm reading this excellent article regarding razor template delegate. http://www.prideparrot.com/blog/archive/2012/9/simplifying_html_generation_using_razor_templates
While I understand how it's used, as in
Func<dynamic, HelperResult> variable = #<var>#item.ProductName</var>
My question is, how exactly razor engine translated "#<var>#item.ProductName</var>" into a delegate in the background? As in
Func<dynamic, HelperResult> variable = delegate(dynamic x)
{
(what goes on in here?)
}
is #item a reserved keyword that razor parses out? Can it be used in any other convention? say #column or #row or any other ways?
Thanks a lot. Like I said, i'm more interested in how razor view engine translated the template statements into actual code in the background.
[Edit]. Thanks to Brad for pointing out Andrew's article. so above statement "#<var>#item</var>" will translate into
Func<dynamic, HelperResult> variable = delegate(dynamic item)
{
return new Microsoft.WebPages.Helpers.HelperResult(__writer => {
#__writer.Write(" ");
#__writer.Write("<var>");
#__writer.Write(item.ProductName); <--- what's happening here?
#__writer.Write("</var>");
}
So I see razor automatically parses out #<var> and </var> into separate strings and such, my question in regards to "item.ProductName" is..suppose "item" is a "Proudct" type, then is the following what razor is trying to do?
First, razor parses "#item.ProductName" separated by comma ".", get "item" and "ProductName".
Then because of the "dynamic" parameter, in the background, .NET will attempt to find the value of the property "ProductName" of the item "Product"?
Thanks
Never mind. I don't know why i didn't make the connection.
I asked another question in regards to DynamicObject DynamicObject? How does the following code work? and #Alxandr explained this regarding "dynamic". So essentially, it becomes
dynamic item = new Product(...);
String ProductName = item.ProductName;
So in essence, "dynamic" parameter in the background use CSharpGetMemberBinder and through reflection, figure out the "ProductName" of the object "Product".
razor template has a pretty brilliant design

MVC 3: HTML.DisplayFor and the Lambda expression

I have found other topics regarding this, but none of them made me understand what I'm trying to figure out.
I am studying MVC 3 and I have problems wrapping my head around the Lambda expressions that go with #HTML.DisplayFor() in the scaffolding template.
I am working with the MvcMusicStore sample application, the view created by the scaffolding looks like this:
#model IEnumerable<MvcMusicStore.Models.Album>
...
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Genre.Name)
</td>
I understand that DisplayFor is a extension method to HTML and that it takes an expression as a parameter. I researched Lambda expressions and understand them in other contexts such as with enumerable strings. For example:
cities.Where(s => s.StartsWith("L"));
What boggles my mind is, that in the second example, the first part of the expression (s) is actually used in the second part (s.startswith..), so it makes sense and I get what the program is doing with it. But this is not the case in the MVC 3 view. modelItem is not beeing used anywhere. I tried renaming just "modelItem" to whatever like so:
#Html.DisplayFor(iAmWearingShortPantsToday => item.Genre.Name)
This code works just as well, displaying all the items and values correctly. This makes me doubt that in this case "modelItem" is actually used for anything. But then, why is it there?
I have also tried to translate this Lambda expression into a delegate (which I believe it is the short form for), like this:
#(Html.DisplayFor(delegate(IEnumerable<MvcMusicStore.Models.Album> modelItem) { return item.Genre.Name; }))
This however, does not work. The resulting error is:
CS1946: An anonymous method expression cannot be converted to an expression tree
Hence my question:
What is "modelItem" good for? What does HTML.DisplayFor() do with modelItem? Are there other cases maybe, where this first part of the expression becomes significant?
If it's possible to translate this expression into a proper delegate, that might also help me to understand what's exactly going on.
Thanks very much
I understand your confusion. Actually modelItem variable is never used in the lambda expression. What is used is the item variable which is captured in a closure from the outer context. The item variable is simply the local variable defined in the foreach loop.
By the way when you see an expression like modelItem => item.Genre.Name in ASP.NET MVC view, that's usually a sign for a bad coding practice. Avoid it. The internet is swarming with such bad examples. Really. I am sick of seeing this. Please help me eradicate this practice.
Here's an alternative method in which the variable is actually used:
#model IList<MvcMusicStore.Models.Album>
...
#for (var i = 0; i < Model.Count; i++) {
<tr>
<td>
#Html.DisplayFor(x => x[i].Genre.Name)
</td>
...
</tr>
}
And even better. Why write loops at all? When we can directly use editor/display templates:
#model IEnumerable<MvcMusicStore.Models.Album>
...
#Html.DisplayForModel()
and then define a display template that will automatically be rendered by ASP.NET MVC for each element of the model collection (~/Views/Shared/DisplayTemplates/Album.cshtml)
#model Album
<tr>
<td>
#Html.DisplayFor(x => x.Genre.Name)
</td>
...
</tr>
Not sure I can answer your question, but the right part is used to tell DisplayFor which property is will create a display for. It is used to determine the type of the property (used to select how to display the value) and the value.
If used in a TextBoxFor, it is also used to retrieve the name of the property, so the textbox can get the proper name.
I think that the expression in a lot of the html-helper extension methods is actually a bit odd, but it's the easy way of letting the code get all these information, without you having to write it down as string parameters (that would fail if you changed anything, without giving a compile-time error).

MVC 4 - How to pass a template to an html helper method

I need to have all my scripts at the bottom of the page, problem is when I have a Partial View I cannot use the "RenderSection" approach. Found a great example of how to add a HtmlHelper extension which takes the name of a script file, loads into a stack, then another helper renders that out on the base layout:
Razor section inclusions from partial view
That's great - but I don't want to have to create an entire JS file for a little chunk of script, or maybe even HTML, that I want to drop in. And I don't want to pass it all as a string, I want the nice formatting and intellisense, so I want to use a template ie:
#{Html.AddScript("test", #<text>
<script type="text/javascript">
function RefreshPreview() {
$('#AutoReplyHtml_Preview').html(
$('#htmlTemplate').html()
.replace('##MESSAGE_TITLE##', $('#AutoReplySubject').val())
.replace('##PRE_HEADER##', $('#AutoReplyPreHeader').val())
.replace('##MESSAGE_BODY##', $('#AutoReplyHtml').val())
);
$('#AutoReplyPlainText_Preview').html(
$('#plainTextTemplate').html()
.replace('##MESSAGE_BODY##', $('#AutoReplyPlainText').val())
);
}
$(document).ready(function() {
RefreshPreview();
});
</script>
</text>);}
Problem is - how to I get the value of the template into my method, I have this code which complies, but no clue how to get the data out of the "code" parameter:
public static string AddScript(this HtmlHelper htmlHelper, string title, Func<object, object> code) {
var ctx = htmlHelper.ViewContext.HttpContext;
Dictionary<string, string> scripts = ctx.Items["HtmlHelper.AddScript"] as Dictionary<string, string>;
if (scripts == null) {
scripts = new Dictionary<string, string>();
ctx.Items.Add("HtmlHelper.AddScript", scripts);
}
scripts.Add(title, code.ToString()); //Doens't work!
return string.Empty;
}
How do I need to tweak the delegate parameter to get the value inside the template??
The helper architecture is designed so that it can accomodate scenarios where you are providing a template that will operate, for example, on each item in a list. In such a scenario, you'd of course want to be able to pass it the "current" item when iterating through the list.
However, in other scenarios (such as yours), there is no current item. Yet, as you've discovered, you still have to declare a delegate as a parameter to your method that defines a method that consumes one parameter. That's ok -- since you're not consuming that argument in your helper (you don't make use of the somewhat magical item parameter in your template) you can just pass it null in your implementation. Preferably, declare your parameter as a Func<object, IHtmlString> rather than a Func<object, object>, but regardless, just invoke code(null).ToString() to get the HTML-encoded string you need to render.

Categories

Resources