Nested layouts?

Hello,

Is it possible to have some kind of nested layouts (like partials in
Rails proper)?

Example: I have a page with a given layout. it has 3 parts, Body with


-----

and the two other parts (Main & Left) being the ones with content.
The thing is, if I want to have another page with the schema, the
content of Body must be deplicated in the new page to get the same
layout.

The goal is to have what is in Body as a kind of template while still
inheriting the “global” layout itself (which define css and snippets
and all that).

Is it possible? Did I missed something obvious in the architecture?

Thanks,

Ollivier R. wrote:

Is it possible to have some kind of nested layouts (like partials in
Rails proper)?

I’m having trouble visualizing how that would work. Can you show me how
you would visualize a layout which used another layout?


John L.
http://wiseheartdesign.com

On 9/1/06, John W. Long [email protected] wrote:

I’m having trouble visualizing how that would work. Can you show me how
you would visualize a layout which used another layout?

I’m more looking at a way to render partials or template.

For the moment, I have

/ – (layout: plain)
|
±- page1
| |
| ±- body (layout: two-colums)
| ±- left
| ±- main
|
±- page2
| |
| ±- body (layout: two-colums)
| ±- left
| ±- main

If I need to add a page that will look like page1 or page2, I have to
duplicate the “body” part.

What I’d like to say is: here is that page with layout “foo” but of
type “bar”, being of type “bar” means that it will have two parts, one
named “left” and the other “main”.

i.e. don’t duplicate the code in “body”.

The analogy being in Rails, you have the layout and partials.

Is that better?

Maybe I’m misunderstanding the radiant way to do it…

Can you not have the same result simply by having a “body” page_part in
the
, and then not have a “body” page_part in the actual pages. Radiant
will
look for the page_part either in the page you are trying to show or in
its
parent.

If you need a different “body” for your root, then just create a hidden
page
with the “” slug put your template in the “body” page_part, and create
your
pages under tis hidden page.

Dror

Ollivier,

Did you consider creating two layouts, one plain and one two-column? I
understand it’s not the prettiest answer, but the 80% rule might apply
in
this case.

Another alternative would be to use some branching tags like
“if_content” or
“if_url” to determine which sub-layout to use, and use a snippet to
encapsulate that sub-layout piece.

Sean C.
seancribbs.com

That’s an interesting solution, in principle similar to the solution I
had for the same problem. However, there are some very un-Rails-ish
statements in your tag implementation that make your intentions
unclear. For example, you find PageParts by their page_id, when really
you should use the existing association:

ancestor_part_names = ancestor.parts.map(&:name)

Next, you use Array.detect where include? will suffice:

if ancestor_part_names.include?(layout_name)

Radiant’s page model offers a shortcut method to getting a part by name,
so your next detect call is also unneeded. In fact this whole block of
statements can be replaced by one line:

tag.locals.page = ancestor if ancestor.part(layout_name)

Page#part will return nil if no matching part is found. Better yet, use
find/detect instead of each:

tag.locals.page = page.ancestors.find {|p| p.part(layout_name) }

There are several other things in your tag definitions that could use
attention, I’ll probably make a pass at them later.

Sean

Thank you very much, Sean.

To all interested here is a cleaned up version of r:layout

tag 'layout' do |tag|
    layout_name = tag.attr['layout_name'] || "layout"
    page = tag.locals.page
    page_part_names = page.parts.collect(&:name)
    parts_for_layout = tag.attr['parts'] || 

page_part_names.collect{|part| part != “body” || part != layout_name }
og_page_id = page.id
tag.locals.page = page.ancestors.find{|p| p.part(layout_name) }
if !page_part_names.include? layout_name
tag.render(‘content’, {“part” => layout_name, “parts_page_id” =>
og_page_id})
end

I have revived this post in order to get comments on my implementation
of nested layouts. I created custom tags to allow for this. Here is my
solution to the scenario given above.

First, in “/”, create a page part called “layout” to be used as the
nested layout. It will look like this:

<r:layout_part name=“left”>

<r:layout_part name=“main”>

Then edit the “body” page part of “page1” and “page2” to look like this:

<r:layout />

The code behind r:layout and r:layout_part follows:


# the layout is actually a page part
# if the named page part is not a part of the current page
# ancestors are searched until the named page part is found
tag 'layout' do |tag|
    ret_val = ""
    layout_name = tag.attr['layout_name'] || "layout"
    page = tag.locals.page
    page_part_names = 

page.instance_variable_get(:@parts).collect(&:name)
parts_for_layout = tag.attr[‘parts’] ||
page_part_names.delete_if{|part| part == “body” || part == layout_name }
found = nil
og_page_id = page.id
if !page_part_names.include? layout_name
page.ancestors.each do |ancestor|
pp =
PagePart.find_all_by_page_id(ancestor.instance_variable_get(:@attributes)[“id”],
:include => [:page])
ancestor_part_names = pp.collect(&:name)
if ancestor_part_names.detect{|part| part == layout_name
} && !found
tag.locals.page = pp.detect{|p| p.name ==
layout_name}.page
found = 1
end
end
tag.render(‘content’, {“part” => layout_name,
“parts_page_id” => og_page_id})
else
tag.render(‘content’, {“part” => layout_name})
end
end

tag 'layout_part' do |tag|
    part_to_render = tag.attr['name']

    # this mess is necessary; trying to get tag_binding_stack 

directly from the context returns a different array
page = tag.locals.page
response = page.instance_variable_get(:@response)
template = response.instance_variable_get(:@template)
controller = template.instance_variable_get(:@controller)
pagee = controller.instance_variable_get(:@page)
context = pagee.instance_variable_get(:@context)
tbs = context.instance_variable_get(:@tag_binding_stack)

    parts_page_id = tbs.last.attributes["parts_page_id"]

    # if the layout is in the same page as the parts being laid out
    # the parts_page_id will not have been specified, so do not 

reset the tag page
unless parts_page_id.blank?
parts_page = Page.find(parts_page_id)
tag.locals.page = parts_page
end

    # e.g. name of part is specified in the tag as 'blah'
    # but the page calling the layout tag which wraps this tag does 

not have a ‘blah’ part
# in such a case don’t display this tag
# NOTE: there is likely a more efficient way to do this check
than a db query
pp = PagePart.find_by_name_and_page_id(part_to_render,
parts_page_id || tag.locals.page.id)
tag.expand unless pp.blank?
end


Thank you in advance for any criticism/comments.