razor with MVC4.0 helper output with method call - c#

#helper GetString()
{
#string.Format("string_{0}","foo");
}
Above code will not compile in ASP.NET MVC 4.0 with Razor 2.0. But if I remove '#' before string.Format, then the code compiles, but the string will not be outputed to the generated HTML. Why's this? In MVC 3.0 with Razor 1.x, above code works as expected. I walkaround this with below code by introducing a variable
#helper GetString()
{
var url = string.Format("string_{0}","foo");
#url
}
Why is this?

#helper GetString()
{
#string.Format("string_{0}","foo");
}
Above code will not compile in ASP.NET MVC 4.0 with Razor 2.0.
That is because the razor engine will see that you have a construct (e.g. string in your example) and you are using the # redundantly. That won't compile as you experienced it.
But if I remove '#' before string.Format, then the code compiles, but
the string will not be outputed to the generated HTML.
Nothing will be outputted because you have not told the razor engine what to write. You just did some code. It is similar to doing in c# and doing a string.Format and not assigning it to a variable.
I walkaround this with below code by introducing a variable
In connection to what I've mentioned earlier, the minute you introduced a variable and wrote it, you are now telling the razor engine to output something. That is similar to a c# console app, wherein:
you did a string.Format: string.Format("string_{0}","foo");
assign its output to a variable: var url = string.Format("string_{0}","foo");
printed the variable: #url
You can read this blog post to better understand how the razor engine works with regards to the usage of #.
Now for an alternative to what you are doing, you can do this:
#helper GetString()
{
<text>#string.Format("string_{0}", "foo")</text>
}

Related

Call Component.InvokeAsync() from within an IViewComponentHelper extension method

I have a solution which has both an ASP.NET Core 3.1 web application project as well as a Razor Client Library (RCL) project. I am trying write a view component which will be distributed with the Razor Client Library, but can be referenced from the ASP.NET Core web application.
I can successfully render this ViewComponent when I call the InvokeAsync() Tag Helper on the web application's _Layout.cshtml:
#await Component.InvokeAsync("NavBar", new {})
But I want to make it so that the RCL is not dependent on a string name provided on the web application. Instead, I would like to call the ViewComponent via an extension method like this:
#{ await Component.RenderMyViewComponentAsync(); }
To me, this way is more beneficial to whoever will use this shared RCL Library, as they don't need to specify the exact name of the ViewComponent, and can rely on IntelliSense to complete the extension method.
I have created a helper class that takes the ViewComponent object and simply calls its InvokeAsync() method within an extension method:
public static class PartialHelper
{
public static async Task<IHtmlContent> RenderMyViewComponentAsync(this IViewComponentHelper vcHelper)
{
return await vcHelper.InvokeAsync("NavBar", new { });
}
}
And, of course, inside of NavBarViewComponent.cs, I have implemented the InvokeAsync() method:
public class NavBarViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
return View();
}
}
Here's the problem, though: I'm not seeing my view render on the screen from the latter method, even though both ways of doing it will still hit my NavBarViewComponent.InvokeAsync().
From what I see, both ways of returning the ViewComponents are functionally equivalent and use the same methods, except #{ await Component.RenderMyViewComponentAsync(); } goes through a helper function first.
I've tried debugging both ways, but to no avail. I am stuck!
Is there any way to achieve what I'm asking for here? If any clarification is needed, please ask.
Your extension method is actually working exactly like it should. The issue is in how you're calling it from your _Layout.cshtml. You're using the following syntax:
#{ await Component.RenderMyViewComponentAsync(); }
That treats it as though it's within a script block, instead of rendering the IHtmlContent to the view. The IHtmlContent is correctly returned, but it isn't assigned to anything or written to the output.
Instead, you simply need to use the same style syntax you used when calling InvokeAsync():
#await Component.RenderMyViewComponentAsync()
Then this will render exactly like you're expecting.
Had the same issue with the extension method, so thank you for the answer.
One addition - instead of using a string, use the generic to catch errors at compile time:
await Component.InvokeAsync<NavBar>();
or directly from the controller:
public IActionResult Index()
{
return ViewComponent(typeof(NavBar));
}
In addition to answering your specific question, I also want to answer the question behind your question. Your general objective seems to be the simplification of the syntax, without relying on strings which don't provide any design- or compile-time validation.
I really like your approach of using extension methods as a solution for this, and will likely borrow that myself. But another approach that solves a similar objective is to invoke your ViewComponent as a Tag Helper:
<vc:NavBar />
This gives you basic IntelliSense support in Visual Studio, and is much cleaner than the InvokeAsync() approach.
That said, it's worth noting that the Tag Helper approach does have a couple of critical limitations, as I've discussed in a previous answer:
It doesn't support excluding any optional parameters you may have defined on your InvokeAsync() method*.
Neither Visual Studio nor the compiler will provide (obvious) warnings if you're missing any parameters.
Given that, your approach of using extension methods may still be preferable if your view components have any parameters—and, especially, if any of those parameters have logical defaults.
*Note: As of ASP.NET Core 6 Preview 6, View Components invoked as Tag Helpers will now honor optional parameters (source).

Razor engine display angle brackets in markdown [duplicate]

I am trying to generate emails with HTML content. this content has already gone through sanitation so I am not worried in that regard, however when I call:
Razor.Parse(template, model);
on the following Razor template:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<body>
#(new System.Web.HtmlString(Model.EmailContent))
</body>
</html>
the email that is outputted is HTMl encoded, but I need it decoded. How can I accomplish this?
RazorEngine, like MVC's Razor View Engine, will automatically encode values written to the template. To get around this, we've introduce an interface called IEncodedString, with the default implementations being HtmlEncodedString and RawString.
To use the latter, simply make a call to the inbuilt Raw method of TemplateBase:
#Raw(Model.EmailContent)
FYI I have a fork that includes the #Html.Raw(...) syntax here:
https://github.com/Antaris/RazorEngine/pull/105
I am using RazorEngine 3.8.2 and #Raw(Model.Content) is working perfectly fine for me.
If you have a custom base class for your templates, you can code Write method to behave similar to normal MVC template: if the output value is IHtmlString it should not encode it.
Here's the code I'm using in my TemplateBase class:
// Writes the results of expressions like: "#foo.Bar"
public virtual void Write(object value)
{
if (value is IHtmlString)
WriteLiteral(value);
else
WriteLiteral(AntiXssEncoder.HtmlEncode(value.ToString(), false));
}
// Writes literals like markup: "<p>Foo</p>"
public virtual void WriteLiteral(object value)
{
Buffer.Append(value);
}
Built a wrapper for RazorEngine that adds in support for #Html.Raw() and #Html.Partial()
https://github.com/b9chris/RazorEngineComplete

What is #Html.IsSelected(controller: "Dashboards")?

I am vb.net familiar. Have a razor c# piece of code. Went through pages of documentation but I cannot find out what does this code do?
<li class="#Html.IsSelected(controller: "Dashboards")>
I understand that runs function IsSelected of Html class but what is the meaning of argument passed to it?
if I understood correctly you want to under stand what this does
#Html.IsSelected(controller: "Dashboards")
# tells razor to output the following to the html code by executing
Html.IsSelected
the parameter is a Named or optional parameter. It is basically saying sets the value of parameter with name "controller" to "Dashboards"
The function's definition might be like
IsSelected(int notUsed = 0, string notUsed2 = null, string controller = "dead beef")
so that you can save yourself some typing instead calling IsSelected(0, null, "Dashboards")

Syntax Error Passing Razor Markup As Parameter

I'm trying to write a custom control and associated html helper that can have it's output changed by providing a template. I've found examples of this on the web (example) but keep hitting upon a strange syntax error that I don't understand.
Here's a simplified version of what I've got so far:
Method signature that takes the razor template
public IHtmlString Template<T>(Func<T, object> template) {
// Implementation not important
}
Calling the method in my view:
#Html.Control(Of Integer)().Template(#<div>#item</div>)
However, I keep getting a syntax error which is showing on the closing bracket of the Template method. I can't figure out what I'm doing wrong here nor could I find any similar problems else where. Does anyone have any ideas what's going wrong here?
Also yes I realise that the helper is written in c# and the razor is in vb sadly that's the way things are round here.

RazorTemplateEngine parser produces unusual syntax tree

I was looking for a way to to reuse my MVC Razor views as javascript templates for client side rendering, and found this library (Razor Client Template) which parses razor views into javascript functions.
It doesn't play ball with Razor engine version 2, and a little digging shows this is because the Razor engine's syntax tree has been overhauled. In an attempt to modify things, I've found some weird results from the RazorViewEngine parser.
I have a very simple view, like so:
#model Justis4.Models.EntityModel
<div>
#Model.PropertyOne
#Model.PropertyTwo
</div>
The razor client template library starts off with:
var host = new RazorEngineHost(newCSharpRazorCodeLanguage());
var engine = new RazorTemplateEngine(host);
var parserResults = engine.ParseTemplate(razorTemplate); //from string reader
var doc = parserResults.Document;
and then goes off to start parsing the resulting syntax tree into a javascript function.
When I debug through the syntax tree, I see some odd stuff. As I understand it, the Razor engine splits the view into "blocks" and "spans" of different types. But as in the picture, the model declaration at the top has been parsed as markup, not code. There are similar oddities, and as a result, the rest of the parsing to javascript fails.
The standard razor parser does not recognise the #model keyword. The #model keyword comes from the MvcCSharpRazorCodeParser class in the System.Web.Mvc assembly.
The main functionality for this comes from SetModelTypeCodeGenerator. This class makes use of the core razor engine class, SetBaseTypeCodeGenerator to change the base type for the razor view from the default WebViewPage to WebViewPage<Model>.
Solutions
Use the #inherits keyword instead e.g. #inherits WebViewPage<EntityModel>.
Or, add a reference to System.Web.Mvc and use a custom language change the code parser to MvcCSharpRazorCodeParser. You will need to set the RazorEngine.DefaultBaseClass property to a non-generic version of the base class you intend to use, e.g. for WebViewPage<T> you would set engine.DefaultBaseClass = "System.Web.Mvc.WebViewPage".
class MvcCSharpRazorCodeLanguage : CSharpRazorCodeLanguage
{
public override ParserBase CreateCodeParser()
{
return new MvcCSharpRazorCodeParser();
}
}

Categories