Why local function in Razor View doesn't compile? - c#

I did some reading and found out that you can now have local functions in razor views: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-3.0#razor-code-blocks
#{
void RenderName(string name)
{
<p>Name: <strong>#name</strong></p>
}
RenderName("Mahatma Gandhi");
RenderName("Martin Luther King, Jr.");
}
And that looks great. But, for some reason this doesn't compile on my machine. Why is that? My target framework is .NetCore 3.1 and Visual Studio 2019 16.6.0. There are some error messages:
"Type or namespace definition, or end-of-file expected" - on the very first line (#using statement) and then:
"Invalid expression term '<'" on the line with HTML.
What is wrong with that?

Functions must be declared inside a #functions block in Razor pages.
Here is a related post.
Is this working?
#functions{
void RenderName(string name)
{
<p>Name: <strong>#name</strong></p>
}
}
#{
RenderName("Mahatma Gandhi");
RenderName("Martin Luther King, Jr.");
}

OK, the solution was really simple. I had a warning that was saying: "Detected Razor downgrade". I just had to remove reference to Microsoft.AspNetCore.Mvc (2.2) which had been added automatically while creating the project in one of previous versions of VisualStudio.

Related

Razor Pages override route getting RoutePatternException despite seemingly correct

I am using Razor Pages, and everything has been going smoothly so far.
Now I wish to create a page with an override route. Like the override routes that are shown possible here.
I am, however encountering the following exception, despite I don't seem to have the issue in my code that it describes:
RoutePatternException: There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character.
I must be somehow misunderstanding how this routing works, but I haven't been able to find someone encountering the same issue in my preliminary searches.
This is my entire code on this page so far:
#page "/layouts/{layoutId:int}/save/{revisionId:int}"
#model Project.Web.Pages.TenantBased.Layouts.SavePageModel
#{
Layout = "_TenantLayout";
ViewData["Title"] = "Title";
}
And this is the code-behind:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Legalit.Web.Pages.TenantBased.Layouts
{
public class SavePageModel : PageModel
{
public void OnGet(int layoutId, int revisionId)
{
}
}
}
This gives the following exception when running the project:
RoutePatternException: There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character.
If I remove the first / from the route as so:
#page "layouts/{layoutId:int}/save/{revisionId:int}"
Then it stops generating the exception, but I of course get the wrong routing from it. Now my page is reachable by the directory path with this route added to the end of it.
I am using .NET 6.0.
The project type is Microsoft.NET.Sdk.Web
The project had a custom Convention in the Program.cs that was specified under the AddRazorPagesOptions configuration method.
After removing this, it worked fine.

MVC4 URLHelper working, but HTMLHelper is not. What am I doing wrong

In a previous post I asked a question about getting started with helpers. I was successful but when I tried to use the technique 1. To write a different helper based on Html.RenderAction, and 2. To pass in my own custom helper I got errors once they were exported to App_Code.
Again, to emphasise, they work inline, but not when exported to App_Code.
Here is the original code:
Many parts of my code have only the following:
<section class="Box">
#{ Html.RenderAction("PageOne"); }
</section>
Many other parts have this:
#if (#Model.PageTwo)
{
<section class="Box">
#{ Html.RenderAction("PageTwo"); }
</section>
}
So my first step was to extract out into an inline helper the following which could be used in all of my code blocks above:
#helper Item(HtmlHelper html, string action, string htmlClass)
{
<section class="#htmlClass">
#{ html.RenderAction(action); }
</section>
}
The helper above allows me to replace all the code blocks that look like the first code segment above with this line:
#Item(Html, "PageOne", "Box")
Then I went on to write the second helper which looks like this:
#helper Item(HtmlHelper html, string action, string htmlClass, bool test)
{
if (test)
{
#Item(html, action, htmlClass)
}
}
This helper allows me to replace all the code blocks that look like the second code segment above with this line:
#Item(Html, "PageTwo", "Box", #Model.ShowThisTorF)
My main question once again is, this works inline, so why not when I remove it to App_Code.
Once I move it to App_Code I get the following errors:
The first problem is regarding adding a using reference (because HtmlHelper is ambiguous) to which I add the following line of code:
#using HtmlHelper=System.Web.Mvc.HtmlHelper
This removes the first error but then I get another error:
System.Web.Mvc.HtmlHelper does not contain a definition for
'RenderAction' and no extension method 'RenderAction' accepting a
first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are
you missing a using directive or an assembly reference?)
I have also tried the other reference but with no result:
#using HtmlHelper=System.Web.WebPages.Html.HtmlHelper
Another problem that I am having is that I don't think the second block will work once I get the first one working. Even though it worked fine inline.
Also, I know its obvious, but if I don't say it here, someone will ask it in their answer. When I moved it out to the file App_Code, I did indeed add the file name prefix as required so the one line lumps of code looked something like:
#Helpers.Item(Html, "PageOne", "Box")
#Helpers.Item(Html, "PageTwo", "Box", #Model.ShowThisTorF)
Thanks for any help with this.
The correct HtmlHelper inside helpers in the App_Code directory is the System.Web.Mvc.HtmlHelper.
Because RenderAction is an extension method you need to add a using for the namespace where it is declared which is #using System.Web.Mvc.Html
So this should work assuming your file is named Helpers.cshtml and in the App_Code directory:
#using HtmlHelper=System.Web.Mvc.HtmlHelper
#using System.Web.Mvc.Html
#helper Item(HtmlHelper html, string action, string htmlClass)
{
<section class="#htmlClass">
#{ html.RenderAction(action); }
</section>
}
#helper Item(HtmlHelper html, string action, string htmlClass, bool test)
{
if (test)
{
#Item(html, action, htmlClass)
}
}
And the usage:
#Helpers.Item(Html, "PageOne", "Box")
#Helpers.Item(Html, "PageTwo", "Box", #Model.ShowThisTorF)

MVC Helpers Working inline, but not in App_Code

Originally I repeated lines of code for each menu item and just hard coded the various menu item values but then I came across helpers and taught I would give it a try. Now 6 lines of code (for each menu item) are reduced to one (for each item), and I have a single place to go to alter anything instead of changing it in 5 places. All great stuff. Here is the code:
#helper MenuItem(string action, string controller)
{
<a href="#Url.Action(action, controller)" id="#controller">
<div class="MenuItem">
<img src="#("/XXX.YYY.Web/Content/Images/Icons/Menu/mnu"+controller+".png")" /><br />
//I had to put the XXX.YYY as a literal string because the ~ didn't work, it was quoted literally also instead of showing the home folder.
#controller
</div>
</a>
}
My problem is that it works when I use it inline, say at the top of my _Layout.cshtml with the following lines of code:
#MenuItem("Index", "Home")
#MenuItem("Index", "Chart")
But when I remove it out to a generic helper called LayoutHelpers.cshtml under the App_Code folder so I can reuse it, and alter the code accordingly as follows:
#LayoutHelpers.MenuItem("Index", "Home")
#LayoutHelpers.MenuItem("Index", "Chart")
Note: Nothing in the actual helper changed. Only the above 2 lines in the _Layout.cshtml file changed.
When I make those changes I get the following error:
Compilation Error
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS0103: The name 'Url' does not exist in the current context
Source Error:
Line 3: #helper MenuItem(string action, string controller)
Line 4: {
Line 5:
Line 6:
Line 7: ##
Now the curious thing is, notice how it works on line 7 "mnuHome.png" as opposed to mnucontroller.png. Yet it says that line 5 is in error.
I also have a problem with the ~ not working in the helper. ie. the ~/Content is shown as a literal string instead of it being compiled to a proper path which should always point to the home folder of the app.
Following is a link that I am using for reference:
http://weblogs.asp.net/jgalloway/archive/2011/03/23/comparing-mvc-3-helpers-using-extension-methods-and-declarative-razor-helper.aspx
Specifically less than 1/4 of the way down the page under the heading "Razor Declarative Helpers". From here on.
Thanks in advance for your help.
The standard helpers (such as UrlHelper and HtmlHelper) are not available in a Razor inline #helper. If you need to use it will need to pass the UrlHelper as parameter to your helper:
#helper MenuItem(UrlHelper url, string action, string controller)
{
<a href="#url.Action(action, controller)" id="#controller">
<div class="MenuItem">
<img src="#url.Content("~/XXX.YYY.Web/Content/Images/Icons/Menu/mnu"+controller+".png")" />
<br />
#controller
</div>
</a>
}
and then when calling pass the correct instance:
#LayoutHelpers.MenuItem(Url, "Index", "Home")
#LayoutHelpers.MenuItem(Url, "Index", "Chart")

Using custom html helpers in an ASP.NET MVC 3 Application

So, for some hours now, I have been trying to do something that I thought - and still think - should be trivial. Basically, I created a Html helper that I needed to use to apply some CSS attribute to the selected menu of an ASP.NET MVC 3 Application. Here is my Html helper:
namespace MVCUI.Extensibility
{
public static class HtmlHelpers
{
public static MvcHtmlString MenuLink(
this HtmlHelper helper,
string text,
string action,
string controller,
string selectedCssClass,
object routeValues,
object htmlAttributes)
{
var attributes = new RouteValueDictionary(htmlAttributes);
if (!String.IsNullOrWhiteSpace(selectedCssClass))
{
var contextController = helper.ViewContext.RouteData.Values["controller"] as String ?? "Home";
var contextAction = helper.ViewContext.RouteData.Values["action"] as String ?? "Index";
if (String.Compare(
String.Format("{0}/{1}", controller, action),
String.Format("{0}/{1}", contextController, contextAction), true) == 0)
{
var cssValue = String.Empty;
if (attributes.ContainsKey("class"))
cssValue = attributes["class"] as String ?? String.Empty;
cssValue = cssValue.Trim();
if (cssValue.Length > 0)
cssValue = cssValue += (" " + selectedCssClass);
else
cssValue = selectedCssClass;
attributes["class"] = cssValue;
}
}
return helper.ActionLink(text, action, controller, new RouteValueDictionary(routeValues), attributes);
}
}
}
Here is how I am using it from a _Layout.cshtml file:
#Html.MenuLink("Posts", "Posts", "Post", "selected", new { }, new { })
For some really odd reason, I keep getting the error:
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS1061: 'System.Web.Mvc.HtmlHelper' does not contain a definition for 'MenuLink' and no extension method 'MenuLink' accepting a first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are you missing a using directive or an assembly reference?)
Here is what I have tried:
Added <add namespace="MVCUI.Extensibility" /> to <system.web.webPages.razor>/<pages>/<namespaces> section of the Web.config file at the root of the Views folder.
Added #using MVCUI.Extensibility; at the top of the _Layout.cshtml file.
Tried all combinations of (1) and (2) above.
Tried the syntax: #{ Html.MenuLink("Posts", "Posts", "Post", "selected", new { }, new { }); }
Googled, alot! All materials and resources, including our very own stackoverflow, seem to suggest that am doing the right thing.
Even tried setting [assembly: ComVisible(true)] in AssemblyInfo.cs. Well, just in case! ;)
Disclaimer: This is the first time I am trying out a html helper in an ASP.NET MVC 3 application.
Where could I be going wrong? Thanks people.
Like someone pointed out in the comments, there was something different about my setup. In my solution, I had changed the Output path for my projects - including the MVC project - from the default bin\ to something like ..\Library\Build\. No crime there since that setup has worked fine so far.
But, that is what got me into trouble. After I restored the default output path and rebuilt my project the Html helper worked. It continued to work even after I restored back my preferred output path - obviously because the dll in the bin folder got updated.
This would mean that the statement #using MVCUI.Extensibility; in my .cshtml file and <add namespace="MVCUI.Extensibility" /> in the Web.config were referencing an old outdated dll in the bin folder that didn't have the HtmlHelper defined. This bothers still. How would I have them reference the dll in my desired output path?
Anyway, I just thought I should post about the experience and the lessons just in case other people find themselves in similar trouble. Thanks people.
This would happen if the file with the extension method says using System.Web.WebPages (which has its own separate HtmlHelper class) rather than using System.Web.Mvc.

Broken RenderPartial with MVC3

I've come to an MVC3 project I wrote just a week ago which has stopped working and is throwing the following error:
Error 10
The call is ambiguous between the following methods or properties: 'System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(System.Web.Mvc.HtmlHelper, string)' and 'System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(System.Web.Mvc.HtmlHelper, string)'
What is the reason for this? I haven't changed anything in project recently for it to bork. The code I call it in looks like this:
<div class="page-body">
#if(!String.IsNullOrWhiteSpace(ViewBag.ErrorMessage)) {
// Output error message
Html.Raw(ViewBag.ErrorMessage);
} else {
// Render upload form
Html.RenderPartial("_UploadForm");
}
</div>
You are missing # symbols front of your Html.Raw because teh method reutrns a string back hence requires the #symbol
For your knowledge taken from MSDN : The Razor syntax # operator
HTML-encodes text before rendering it to the HTTP response. This
causes the text to be displayed as regular text in the web page
instead of being interpreted as HTML markup.
Please use it this way
<div class="page-body">
#if(!String.IsNullOrWhiteSpace(ViewBag.ErrorMessage)) {
#Html.Raw(ViewBag.ErrorMessage);
} else {
// Render upload form
Html.RenderPartial("_UploadForm");
}
</div>

Categories