50

I'm trying to write a simple declarative html helper:

@helper Echo(string input) {
    @input
}

The helper works fine if I embed it into the page I want to use it on. But if I move it to a separate .cshtml file and place that file in the ~/Views/Helpers directory, my view can't be compiled anymore because the helper is not found. According to Scott Gu's blog article on Razor it should work.

What am I doing wrong?

Chi
  • 163
  • 1
  • 2
  • 12
Adrian Grigore
  • 33,034
  • 36
  • 130
  • 210

7 Answers7

50

The ~/Views/Helpers location as describe in that ScottGu post will not work in MVC 3 RTM. At the time that article was written it was a design idea we were tossing around but ultimately we were not able to implement it.

Putting your helpers in App_Code works but has certain limitations that impact certain MVC scenarios (for example: no access to standard MVC Html. helpers)

marcind
  • 52,944
  • 13
  • 125
  • 111
  • 5
    Does this mean that the declarative HTML helpers are effectively dead? Where's the advantage over a regular partial view? – Adrian Grigore Dec 15 '10 at 19:19
  • They're not dead and we are thinking of providing some add-ons (via MvcFutures, or a nuget package) that will address the problems. But at this point their future is "fuzzy". – marcind Dec 15 '10 at 20:28
  • I dug into this a little bit and have a question/proposal to support `Html.` methods for declarative @helpers in the App_Code folder. Can you have a look and see if it would work? http://stackoverflow.com/questions/5282655/why-is-the-htmlhelper-instance-null-in-a-razor-declarative-helper-method/5282713#5282713 – Drew Noakes Mar 13 '11 at 16:42
  • 3
    Probably a good idea to update the article, as it made me think it's in MVC 3 release. Thank you –  Apr 13 '11 at 14:08
  • @marcind, will this be fixed in the next version of Razor/MVC/WebPages? – Omar May 07 '12 at 18:06
  • 3
    Sorry, no. MVC 4 will not include this feature. Please post feature requests on http://aspnet.uservoice.com/forums/41201-asp-net-mvc – marcind May 08 '12 at 15:09
  • If it's helpful I would suggest that the Razor parser should implicitly pass the html helper as an argument to the helper (similar to DarkGray's answer but only the Html helper [this.Html] and implicitly by the Razor engine) – yoel halb Sep 05 '12 at 22:25
  • 1
    Hi! I have created an idea in ASP.NET user voice related to this: [Support @helper ExtensionMethod(this HtmlHelper html) for views in APP_CODE](http://aspnet.uservoice.com/forums/41201-asp-net-mvc/suggestions/3670180-support-helper-extensionmethod-this-htmlhelper-ht)... help is appreciated in making it come to the front-lines... Thanks! – Miguel Angelo Feb 20 '13 at 02:23
38

Simple workaround:

In each helper add argument WebViewPage page.

App_Code\Test.cshtml:

@using System.Web.Mvc.Html
@using System.Web.Mvc

@helper HelloWorld(WebViewPage page)
{
    @page.Html.Label("HelloWorld")
}

This helper usage in any page:

 @Test.HelloWorld(this)
Owen Blacker
  • 4,117
  • 2
  • 33
  • 70
Serj-Tm
  • 16,581
  • 4
  • 54
  • 61
  • This doesn't appear to work - page.Html does not contain Label (or any of the other helper methods) – Ben Hughes Dec 12 '11 at 04:32
  • helper method Label (and other) is extension, declared in class System.Web.Mvc.Html.LabelExtension. add using System.Web.Mvc.Html – Serj-Tm Dec 22 '11 at 23:24
  • 2
    Worked great in my case, I needed to access the Html.AttributeEncode helper. Thanks DarkGray! – dwhite Feb 16 '12 at 00:12
  • Thanks, that worked! although I don't like the idea of passing `this` each time, works for now, while I find another solution. Thanks again! – yorch Nov 15 '12 at 18:53
9

In App_Code a @helper needs this to work properly:

// These are important for `Html.RouteLink` and such.
@using System.Web.Mvc;
@using System.Web.Mvc.Routing;
@using System.Web.Mvc.Html;
@using System.Web.Mvc.Razor;

@helper SomeHelper()
{
    // Get page and pull helper references from it.
    var wvp = PageContext.Page as System.Web.Mvc.WebViewPage;
    var Url = wvp.Url; // UrlHelper access
    var Html = wvp.Html; // HtmlHelper access
    var ViewBag = wvp.ViewBag;
    // Helper code comes here...
}
CodeAngry
  • 12,760
  • 3
  • 50
  • 57
9

As marcind said, we weren't able to support the ~/Views/Helpers location due to some limitations in our compilation model.

We're working on a better solution for declarative HTML helpers, but it won't make v1. For v1, we have two solutions:

  1. App_Code (which has some problems as Marcin mentioned, but does work)
  2. David Ebbo (member of the team) has a Visual Studio add-in that compiles them into your DLL
Community
  • 1
  • 1
Andrew Stanton-Nurse
  • 6,274
  • 1
  • 28
  • 20
6

I followed the steps listed in Scott's blog as well and wasn't able to get it working either.

I did some searching and found this link: http://dotnetslackers.com/articles/aspnet/Experience-ASP-NET-MVC-3-Beta-the-Razor-View-Engine.aspx#s19-create-custom-helper-method

Followed the steps and it's working. The key seems to be both the App_Code folder as well as using the file name dot helper name when calling the helper.

Brian Ball
  • 12,268
  • 3
  • 40
  • 51
  • I don't believe it's the way Razor helpers are supposed to work, but it is a good workaround. Thanks a lot! – Adrian Grigore Dec 15 '10 at 16:56
  • 1
    Unfortunately the dotnetslackers.com web site no longer exists. If you had quoted some of it or posted the title of the article, I might have found the article elsewhere using Google if it's been published elsewhere. :-( – ChrisW Jan 15 '18 at 21:28
4

I know this is late in the game... But, pass in the current View to the helper function. From the view you have access to Html and the helper functions.

Jim Shaw
  • 181
  • 6
4

Another take on CodeAngry's answer, this enables the helpers for every method in the file.

@using System.Web.Mvc;
@using System.Web.Mvc.Html;
@using System.Web.Mvc.Routing;
@using System.Web.Mvc.Razor;

@functions {
    private static WebViewPage page { get { return PageContext.Page as WebViewPage; } }
    private static System.Web.Mvc.HtmlHelper Html { get { return page.Html; } }
    private static UrlHelper Url { get { return page.Url; } }
    private static dynamic ViewBag { get { return page.ViewBag; } }
}
Community
  • 1
  • 1
Ryan Buddicom
  • 1,131
  • 15
  • 30
  • These functions are not necessary in MVC 5.x (and maybe earlier). Looks like the `HelperPage` base class now has the Page and Html properties built-in, so for example, you can use `Page.Url` and `Page.ViewBag` directly in your helper code. – Bob Meyers Oct 23 '16 at 00:47
  • Are you sure? I still cannot access these helpers in a fresh project on the latest mvc. Maybe intellisense is just telling me lies ;) – Ryan Buddicom Oct 25 '16 at 04:27