Dispatching Orchard shapes to arbitrary zones

(c) Bertrand Le Roy 2011In my LIDNUG demo last week, I showed an interesting technique that I know some people will want to apply to their own stuff.

The scenario is that you want the main content being displayed on the page to render parts of itself outside of the Content zone, typically in a sidebar zone.

My own example was a Buy From Amazon part that displays a badge in the sidebar to enable readers to buy the book being reviewed:The Buy From Amazon part gets displayed in the side bar.

Usually, zones contain widgets, but zones are just shapes, and they can contain any nested shape, not just widgets. Here is the shape we are building from our driver:

var shape = shapeHelper.Parts_BuyFromAmazon(
        ProductSku: part.ProductSku,
        AssociateAccount: accountSettings.AssociateAccount,
        BackgroundColor: accountSettings.BackgroundColor,
        TextColor: accountSettings.TextColor,
        LinkColor: accountSettings.Linkcolor,
        Width: accountSettings.Width,
        Height: accountSettings.Height,
        ContentItem: part.ContentItem);

Nothing fancy here. In order to send that shape to the sidebar, all we need is a reference to the current work context in our part driver. This is easily obtained by injecting a dependency to IWorkContextAccessor.

Once this is done, we can just get the layout shape from the work context, and then access its zones. Once we have the right one, we can add our shape to it:

_workContextAccessor.GetContext()
    .Layout.Zones[zone]
    .Add(shape, position);

Finally, we return an empty driver result rather than the shape, so nothing gets rendered in place:

return new DriverResult();

When the zone where we sent the shape is going to render, our shape will get resolved to a template exactly like a widget would. For all practical purposes, this is like a local widget that your content item gets to create and control.

8 Comments

  • Very cool! The more I get to know Orchard, the more I love it. Keep up the awesome work!

  • Bertrand,
    I was facing the same problem. I wanted to show widget related to specific content and I ended up with different solution (BTW thanks to yours and Randompete's tips on forum).
    I created widget which is content aware by puling from WorkContex (in my case from RouteData) then I created layer with url rule and at the end I placed the widget in this layer.
    IMHO the advantage of this approach is that widget can be move around declaratively in UI.

  • @Naresh: please don't cross-post. Your question will be answered on the CodePlex discussions.

  • Hey Bertrand, I am sorry but the situation as of now is kind of tight so i am desperately trying all possible solutions. please help!

  • Impressive stuff!

    Would it also be possible to add a shape to a zone that I defined in another shape rendering? For instance, in part_one.cshtml I placed the code @Display(Model.ExtendPartOne) and in the PartTwoDriver, I attach it to the ExtendPartOne zone via _work.GetContext().Layout.Zones["ExtendPartOne"].Add(shape, 1);

    Unfortunately this doesn't seem to work...

  • @Lukasvan3L: sure, if you can grab a reference to it (which should be possible by drilling into the shape tree from Layout), you can add to it. I think the problem here is that the zone you are trying to address is not top-level, you just need to drill deeper.

  • I am trying to do the same but can't succeed. In your example you state the following:

    _workContextAccessor.GetContext()

       .Layout.Zones[zone]

       .Add(shape, position);

    But how to determine the value of [zone]. I can't just put zone as value in, right. So how to get a reference to the zone where you want to put the content in?

  • @Mounhim: [zone] is the name of the zone you want to target. For example, ["MyZoneName"].

Comments have been disabled for this content.