Using shapes as Html helpers in Orchard

Tags: orchard, shapes, tutorial, english

Orchard shapes are the basic building block of the whole rendered UI. They are ordinary, although dynamically discovered and created by Orchard framework, Razor (.cshtml) view files. One of the coolest (and also the one that generates most of the beginners’ problems) thing about Orchard is it’s flexible nature by extensive use of C# 4.0 dynamic objects.

Suppose you have the following line of code:

@Display(New.MyShape(Text: “someText”, SomeProperty: someObject))

You may think at first: “What the hell is that?”. After that, I suppose, many of you would try ReSharper or some other tool to find out what’s going on – where is Display method declared, what is the New property and, at last, what is that MyShape thing doing there. Bump! No resultsZdziwienie How can it be that MyShape(…) method is nowhere declared?

What would you say if I told you that this line of code renders the /Views/MyShape.cshtml Razor view file with dynamic model containing two properties: Text and SomeProperty? “No way! This is not the .NET way of doing things.”.

But it is in factUśmiech MyShape(…) is a dynamically created method, which is responsible for rendering MyShape.cshtml file. The named parameters you provide are combined into one dynamic object as properties and passed as a view model into the rendered shape. So inside the MyShape.cshtml file you can make use of Model.Text and Model.SomeProperty properties. Display and New are built-in shape methods responsible for rendering markup (Display) and creation of shapes (New).

And getting to the point – the way Orchard shapes work, resembling ordinary methods with ordinary parameters but with Razor code inside, makes them a perfect tool to use (and reuse) as helpers.

They are even better than ordinary helpers because:

  1. You don’t have to render HTML from C# code (which looks terrible – long and unreadable code) – just use Razor syntax
  2. You don’t have to remember to use certain namespaces (by @using … ) – Orchard makes sure you got your shapes always available
  3. It makes your code more understandable (and concise btw), as shape names are derived from shape .cshtml file names which makes those files easy to find (not always though, but usually it’s true)
  4. They don’t need to be overridden, because all parameters are named/optional – less code, less trouble (take a look into the default MVC HTML helpers – every one has lots of overrides…)

And now, the drawbacks…:

  1. Use of dynamic objects – depending on what you need this can be thought of as a nice feature or a drawback. The better part is that this is what really makes Orchard so flexible and cool Uśmiech. The worse - Forget about Intellisense Smutek on Model property inside .cshtml files. A workaround to this is to cast a specific Model property (you know type of – eg. you know that Model.Text is a string) to that type at the beginning of a .cshtml file and use the strongly typed version instead. Also – forget about Intellisense when calling Display and New methods.

A short summary. If you want to create a reusable helper shape:

  1. Create an appropriate .cshtml file in the /Views folder of your module (eg. /Views/MyHelper.cshtml)
  2. Place an appropriate reusable Razor (HTML + C#) code inside
  3. Use it in your part shapes/shape overrides/field shapes – anywhere you like by using @Display(New.MyHelper(params)), where params are the named parameters you use inside the /Views.MyHelper.cshtml file as Model.param

 

Hope you find it useful in your future Orchard modules!Uśmiech

Cheers!

kick it on DotNetKicks.com

6 Comments

  • Louis DeJardin said

    Great post! There are more tricks buried in there if you can find them, too.

    For example @Display.MyShape(Text: “someText”, SomeProperty: someObject) can be used as a shortcut so you don't need the New. So you can put @Display.Message(Message:"Hello", Type:"Error") somewhere to render a themed Message shape.

    Also - try searching around for [Shape] attributes in the code. In any IDependency component you can add [Shape] public IHtmlString MyShape(string Text, object SomeProperty) {...} methods if you want to render from a function instead of a template.

    They're interchangable, actually. A [Shape] MyShape in a module can be overridden by a MyShape.cshtml file in a view.

    And thanks again for your participation!

  • pszmyd said

    @guest: What do you exactly mean by that? T4 would be a step backwards, because shapes would be wired to specific static typed model (instead of duck-style type handling), which is just what we want to avoid. Themes would need to directly reference all modules to allow shape overriding...

    @Louis: Thanks for the info! I forgot to mention about the pure-code version of Shapes so thanks for pointing this out. This is a nice way of creating shapes when you have a more complex display logic, eg. depending on some other objects, injected via a constructor or generally - more C# code than markup.

    Cheers!

  • guest said

    I mean, after create MyShape.cshtml, we can run T4 and get static type wrapper, instead of dynamically created method, like T4MVC. I think dynamic is bad and should be avoid, intellisense and static check it's what we need.

  • pszmyd said

    Yes, that is possible, but that would kill the theming feature. If theme designer would want to override your MyShape.cshtml file to fit it into his design he'd have to explicitely reference your module (as the static model type is declared there).

    And he'd have to do that for every module which contains shapes he'd want to override - this would render the theme useless as it would mean every user would have to install all overridden modules...

    Dynamic is not always good - I agree - using dynamic just because it's a new feature is pointless. But the case of Orchard is different - there is a very big advantage of using dynamic insteady of static typing in UI - flexibility and module independence.