Referencing @helper functions in App_Code in a module

Topics: Writing modules
Jun 23, 2011 at 5:56 PM

I'm in the process of creating my first Orchard module, and I'd like to use a helper function in my views as described here: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx 

In a normal Razor app, I would put a Helpers.cshtml file in App_Code, and then reference it in my views like @Helpers.HelpMe().

But this doesn't seem to work in a module, presumably because the files in App_Code aren't picked up for dynamic compilation.

Is my diagnosis right? Any suggestions on how to do this?

Coordinator
Jun 23, 2011 at 7:34 PM
Edited Jun 23, 2011 at 7:35 PM

the helpers file does not need to be in app_code. This module for example: http://orchardproject.net/gallery/List/Modules/Orchard.Module.Amazon.Checkout has an AmazonCheckout.cshtml file that defines helpers. It's in a Helpers folder and it works fine.

Then again I should mention that Razor helpers are not the recommended way of doing things in Orchard. Shapes are preferred.

Jun 23, 2011 at 8:34 PM

I had tried placing the file in a different directory, but it seems to treat it slightly different than how it works in App_Code. Here's my code:

@using System.Web.Mvc.Html;
@using System.Linq.Expressions;
@functions
{
    public static HelperResult WriteInputSet<TModel, TItem>(HtmlHelper<TModel> html, Expression<Func<TModel, TItem>> expr)
    {
        return WriteInputFieldCore(html.LabelFor(expr), html.EditorFor(expr), html.ValidationMessageFor(expr));
    }
}

@helper WriteInputFieldCore(MvcHtmlString label, MvcHtmlString textbox, MvcHtmlString validationMessage)
    {
    <div class="editor-label">
        @label
    </div>
    <div class="editor-field">
        @textbox
        @validationMessage
    </div>
}
When this file is in a Helpers directory, Visual Studio throws a squiggle on "return WriteInputFieldCore()", and complains, "Cannot access non-static method 'WriteInputFieldCore' in static context."

Oh well.

But since it sounds like "shapes" are the preferred way of doing this, I'll look into them. 

Jun 24, 2011 at 7:34 AM

drop the static from the WriteInputSet<TModel, TItem> function

Jun 24, 2011 at 5:25 PM

Removing the "static" removes the error, but it then doesn't get picked up by the other views. In other words, the line "@Helpers.WriteInputSet(Html, WriteInputSet(m => m.CompanyTag))" has a red squiggly under "Helpers", with the message, "Cannot resolve symbol 'Helpers'".

Coordinator
Jun 24, 2011 at 8:07 PM

They should be static. OK, so what happens at runtime?

Jun 27, 2011 at 4:37 PM

I get the same error message: 

Server Error in '/OrchardLocal' Application.

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 'Helpers' does not exist in the current context

Source Error:

 
Line 30:             <legend>Account Information</legend>
Line 31:             <!-- @WriteInputSet(m => m.CompanyTag) -->
Line 32:             @Helpers.WriteInputSet(Html, m => m.CompanyTag)
Line 33:             @WriteInputSet(m => m.UserTag)
Line 34:             @WriteInputSet(m => m.Password)


Source File: c:\source\Alanta\working\AlantaWebCorp\src\Orchard.Web\Modules\Alanta.Web.Corp\Views\Account\LogOn.cshtml Line: 32 

Jun 27, 2011 at 6:14 PM

Or here's a better way of asking the question. I was wanting to create a generic helper that does this:

@helper WriteInputSet<TModel>(Expression<Func<TModel, string>> expr)
    {
    <div class="editor-label">
        @Html.LabelFor(expr)
    </div>
    <div class="editor-field">
        @Html.EditorFor(expr)
        @Html.ValidationMessageFor(expr)
    </div>
}

That precise syntax doesn't work, because helpers can't take generics, which is bizarre, but anyway, you can see what I want to do. Is there a way to do that using shapes?

Coordinator
Jun 27, 2011 at 6:34 PM

In your theme, create a file named Foo.cshtml with this template (just the html), then from anywhere in Orchard, call Display.Foo(Expr: what_you_want), and use Expr in your template.

Jun 27, 2011 at 6:48 PM

But Expr will be dynamic, not generic, right? I can't cast it to a generic. And if I want to use the Html.LabelFor(), Html.EditorFor(), and so forth, that pick up the various details from my TModel, I can't simply pass them a dynamic object - I'd have to cast it to something, if only to a generic type specified somewhere - but where do I specify it? (Or am I missing something obvious?)

I suppose I could use just standard Html.Label(), Html.Editor(), and so forth, and do the work to figure out what the various model attributes are - but it seems weird to do that when there are already perfectly good Html.XXXFor() helper methods.

Oct 17, 2012 at 9:36 AM
bertrandleroy wrote:

the helpers file does not need to be in app_code. This module for example: http://orchardproject.net/gallery/List/Modules/Orchard.Module.Amazon.Checkout has an AmazonCheckout.cshtml file that defines helpers. It's in a Helpers folder and it works fine.

Then again I should mention that Razor helpers are not the recommended way of doing things in Orchard. Shapes are preferred.

You need an extension for that (http://visualstudiogallery.msdn.microsoft.com/1f6ec6ff-e89b-4c47-8e79-d2d68df894ec) to preprocess the razor file.

At least I think that is the one.