Search code examples
twiggrav

Grav - Retrieving specific items from page in template


I have the following page structure in Grav:

# Title
## Subtitle
### Subsubtitle

The page structure is always the same and it has just those three items.

How can I retrieve each item (Title/Subtitle/Subsubtitle) separately in Twig template?

The twig template will then do things like:

<p> You typed {{ page.whatever_retrieves_the_title_# }} as title, then {{ page.whatever_retrieves_the_subtitle_## }} as subtitle and finally {{ page.whatever_retrieves_the_subsubtitle_### }} as subsubtitle</p>

What if instead of the above structure I have:

- Title
- Subtitle
- Subsubtitle

The objective is that the user adds just that structure of three items and the twig template use each item to display a more complex layout.


Solution

  • This is Markdown, right?

    # Title
    ## Subtitle
    ### Subsubtitle
    

    You can get the HTML version of the page's Markdown in Twig with {{ page.content }}, as described in Grav's documentation. So you should get something like this:

    <h1>Title</h1>
    <h2>Subtitle</h2>
    <h3>Subsubtitle</h3>
    

    You can use the split and raw filters to extract the contents of those tags. I'm also using the default filter so that there won't be an error if the extraction of the tag contents fails:

    Title is:
    {{ page.content|split('<h1>')[1]|default|raw|split('</h1>')[0] }}
    
    Subtitle is:
    {{ page.content|split('<h2>')[1]|default|raw|split('</h2>')[0] }}
    
    Subsubtitle is:
    {{ page.content|split('<h3>')[1]|default|raw|split('</h3>')[0] }}
    

    Or because Grav seems to provide a regex_replace filter, you could also use it:

    Title is:
    {{ page.content|regex_replace('~.*<h1>(.*)</h1>.*~s', '$1') }}
    
    Subtitle is:
    {{ page.content|regex_replace('~.*<h2>(.*)</h2>.*~s', '$1') }}
    
    Subsubtitle is:
    {{ page.content|regex_replace('~.*<h3>(.*)</h3>.*~s', '$1') }}
    

    If instead you have this:

    - Title
    - Subtitle
    - Subsubtitle
    

    You can again use the split, default and raw filters:

    Title is:
    {{ page.content|split('<li>')[1]|default|raw|split('</li>')[0] }}
    
    Subtitle is:
    {{ page.content|split('<li>')[2]|default|raw|split('</li>')[0] }}
    
    Subsubtitle is:
    {{ page.content|split('<li>')[3]|default|raw|split('</li>')[0] }}
    

    Not very beautiful. :-) If the titles can contain HTML (e.g. ## Hello **world**!<h2>Hello <strong>world</strong>!</h2>) or special characters, you probably need to append |raw to the already long magic spells.