Creating shapes on the fly

(c) Bertrand Le Roy 2011Most Orchard shapes get created from part drivers, but they are a lot more versatile than that. They can actually be created from pretty much anywhere, including from templates. One example can be found in the Layout.cshtml file of the ThemeMachine theme:

WorkContext.Layout.Footer
.Add(New.BadgeOfHonor(), "5");

What this is really doing is create a new shape called BadgeOfHonor and injecting it into the Footer global zone (that has not yet been defined, which in itself is quite awesome) with an ordering rank of "5".

We can actually come up with something simpler, if we want to render the shape inline instead of sending it into a zone:

@Display(New.BadgeOfHonor())

Now let's try something a little more elaborate and create a new shape for displaying a date and time:

@Display(New.DateTime(date: DateTime.Now, format: "d/M/yyyy"))

For an even shorter syntax, we can even use Display directly to create the shape:

@Display.DateTime(date: DateTime.Now, format: "d/M/yyyy")

For the moment, this throws a "Shape type DateTime not found" exception because the system has no clue how to render a shape called "DateTime" yet. The BadgeOfHonor shape above was rendering something because there is a template for it in the theme: Themes/ThethemeMachine/Views/BadgeOfHonor.cshtml. We need to provide a template for our new shape to get rendered. Let's add a DateTime.cshtml file into our theme's Views folder in order to make the exception go away:

Hi, I'm a date time shape.

Now we're just missing one thing. Instead of displaying some static text, which is not very interesting, we can display the actual time that got passed into the shape's dynamic constructor. Those parameters will get added to the template's Model, so they are easy to retrieve:

@(((DateTime)Model.date).ToString(Model.format))

Now that may remind you a little of WebForm's user controls. That's a fair comparison, except that these shapes are much more flexible (you can add properties on the fly as necessary), and that the actual rendering is decoupled from the "control". For example, any theme can override the template for a shape, you can use alternates, wrappers, etc.

Most importantly, there is no lifecycle and protocol abstraction like there was in WebForms. I think this is a real improvement over previous attempts at similar things.

5 Comments

  • Nice picture of the tree built with lego shapes. Orchard feels that way: playing with Lego.

    I used this ad hoc shape technique yesterday to vary the layout based on whether the homepage was requested, or another content page (using the Url Alternates feature of the Designer Tools module).
    It's very common to have a homepage with a different layout than the other pages.

    For example, to vary the layout based on whether the user is on the homepage or not, I created an ad hoc shape "BodyContent" inside Layout.cshtml ("@Display(New.BodyContent())") and created two templates for it: "BodyContent.cshtml" for the content pages layout and "BodyContent-url-homepage.cshtml" for the homepage layout.

    The interesting thing I'd like to share with newcomers to Orchard is that next you can easily render Zones in these templates, by simply calling "@Display(WorkContext.Layout.[NameOfTheZone])".

    Being able to skin any shape using alternates you have ultimate flexibility in creating multiple page templates.

  • Hush, that thing about zones is the second post I'm writing today. How did you know? ;)

  • Ha, looks like Orchard is causing some kind of quantum entanglement effect ;)
    Sweet, looking forward to that second post!

  • Plus, you can use a shortcut on the Display object to New and render a shape in one step:

    @Display.DateTime(date: DateTime.Now, format: "d/M/yyyy")

    Literally the same code running, really, just hitting fewer keys.

  • Oooh, yes, missed that one. Updating the post.

Comments have been disabled for this content.