Displaying a projection with custom layout on various pages

Topics: Customizing Orchard
Mar 26, 2013 at 9:41 AM
As this is my first attempt within this area of functionality this is a question regarding the approach I have taken, rather than a problem trying to get it to work.

Requirement is to utilise the jQuery UI accordion widget to display a list of downloadable press releases (basically a link, title and some meta data) on a page below some descriptive text.

Have created a Content Type for the press release then used a Query with a custom layout deriving from IFormProvider & ILayoutProvider as mention in Skywalker's groovy article Orchard Webshop module part 5. The custom layout provides the markup and wiring for the accordion.

This all works fine so far, however, I am now uncertain with my approach taken for rendering on the page.

The current requirement is to display the accordion below an area of body text. To remedy this I have used a layer and projection widget. The layer rule targets the particular page the accordion should be displayed upon (I like the description stating the concept of layers is the 'when' something should be drawn).
Since this initial work I have used this projection on another page by adding an or statement to the original layer rule.

Has anyone done anything similar, or can you spot any howlers with this solution?

I did attempt to construct a content type based upon an amalgamation of the Page and Projection Page content types. This appeared to require more shape placement adjustment, so I stopped.

Comments, thoughts, criticisms, recommendations please.
Coordinator
Mar 26, 2013 at 6:47 PM
Edited Mar 26, 2013 at 6:48 PM
There is a much simpler solution, use this module: http://gallery.orchardproject.net/List/Modules/Orchard.Module.Contrib.ProjectionLayouts
It add two new types of layouts that will do exactly what you want.

First keep you Press Release content type.
Then create the Query to list them all, order by Created Date.

Now if you want to use layout's properties then you should go with the Raw layout. But if you need flexibility then use the Shape layout.
So for instance use the Shape layout, use a shape name like "ListOfPressReleases" and in the Display Type you can keep SummaryAdmin. What will happen is than when a projection or a widget will render this query, it will use a "ListOfPressReleases" shape, which in turn will look for a "ListOfPressReleases.cshtml" file in your theme. And it will provide you with two variables:
  • the list of content items which have been queried (Model.ContentItems)
  • the shapes for these content items, using the Display Type you selected (Model.BuildShapes())
In this file, you can type something like this:
@foreach(var shape in Model.BuildShapes()) {
    @Display(shape)
}
And you should customize the Content-PressRelease.SummaryAdmin.csml file to define how to render each of the press releases in this view.

You can also add the necessary jQuery script to add the accordion to the list.

Hope you get the idea.
In the near future I'd like to add a way to define this shape directly in the layout properties.
Mar 27, 2013 at 10:25 AM
Groovy, thanks.

I've implemented the layouts module and polished as you say. This has allowed me so simplify a range of widgets I was using.

Anyone have any opinions on the layer placement of these widgets as although it seems correct, it strikes me that as the site grows the mass of layer rules could prove problematic (or slow)
Developer
Mar 27, 2013 at 10:36 PM
It depends on your exact requirements.

If for example you are creating one layer per page because you want on each page an HtmlWidget in the AsideSecond zone, you could consider creating a new content type which is the same as the Page content type but with an additional text field called something like "Aside", and use Placement.info to place this field's shape into the AsideSecond zone.
If you have a widget that needs to show up on a couple of pages, you could create a single layer with a rule something like this: "url(~/page1) or url("~/page2") or ...etc.".
If you want to display widgets based on the content type, there's a new rule for that in 1.x.
And of course you could write your own custom layer rule provider.

Eventually, it will be possible to add widgets to a canvas without having to create layers, but we're not there yet. Although you could create something like that yourself using a custom module. If you do, please share :)
Mar 28, 2013 at 2:32 PM
Many thanks.

I have a number of differing content projections to add so will implement via the first two options. I looked at the new content type rule and don't think it suits at the moment. Creating a custom module could be risky with my project timescales, now if only I could force a projection through the placement.info....

cheers guys....
Oct 15, 2013 at 5:53 AM
Edited Oct 15, 2013 at 5:53 AM
Hey guys, I would like to use the Shape layout, but can I also use one in which I could just use a shape for the whole container? I need some wrappers around my items too.
I see that your contrib part has already been included in 1.7.1
Coordinator
Oct 15, 2013 at 5:31 PM
If you use the shape layout, what prevents you from wrapping the items ? Even in a different shape.
Oct 16, 2013 at 8:13 AM
Hey Sebastien, How do I define that wrapper? it is an alternate of what?

So let's say I have :
<div id="x"><div class="y">
item
item
item
</div></div>
What view do i have to create an alternate of?
Mar 13, 2014 at 3:02 PM
Anyone who's planning to use the shape layout, but can't get it working with the code above...
@foreach(var shape in Model.BuildShapes()()) {
    @Display(shape)
}
Notice the extra (). The first is used to get the Func<>, the second is used to execute it.
Coordinator
Mar 13, 2014 at 7:51 PM
Model.BuildShape is actually a Func, so you just need one set of ()
Coordinator
Mar 13, 2014 at 7:52 PM
or maybe try with var shapes = Model.BuildShapes then iterate over shapes()
Mar 14, 2014 at 12:45 PM
When using one set of (), this is what I get:
Cannot implicitly convert type 'System.Func<System.Collections.Generic.IEnumerable<object>>' to 'System.Collections.IEnumerable' 
Coordinator
Mar 14, 2014 at 9:07 PM
Here is some code I have which works:
@{
    var shapes = Model.BuildShapes;
}
<div class="row">
   <div class="span8">
        <h3>Featured Partners</h3>
    <div class="promo">
            @foreach(var shape in shapes()) {
                @Display(shape)
            }
        </div>
    </div>
 </div>
Mar 17, 2014 at 8:59 AM
Edited Mar 17, 2014 at 8:59 AM
Yes, that does work.

But moving the () to BuildShapes (or iterating directly over Model.BuildShapes()) throws the exception mentioned above. Not sure why. Probably a dynamics thing...

Changed my code to match your example, more readable than ()(). Thanks.
Feb 18, 2015 at 7:19 PM
Model.ContentItem is always empty for me. Also Model.BuildShapes returns empty list. But i can see content items in preview. My query must select all pages.