We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 30585
    • 833 Posts
    Wayfinder is a great snippet for building a dynamic navigation for your site, but sometimes all you need is just a quick and simple way to navigate between sibling pages with Previous and Next links. There are quite a few posts in the forums on this subject, but the solutions offered aren't usually that clear. So I'll propose a new approach using the famous getResources.

    Kudos
    99% of the ideas discussed in this tutoral aren't mine. So kudos to Jason Coward for his enlightening take on using getResources to Build Menus. Big props to YJ Tso for expanding on Jason's initial discussion in a very eloquent and newbie-friendly way. You can find his post here: How-to: Use #MODX getResources for Dynamic Nested Menus

    Our tutorial will be based on a sample website built for a children's book that has one container and 12 children resources beneath it.

    Here's the site structure:

    Home/            # Home page // Not really needed in our tutorial
    |
    |-- Book/                   # Container - Book's summary page. ID: 927 (Yours could be different). 
    |   |-- chapter-1.html      # Pagetitle: My Awesome Book. Template: Book_chapters (44)
    |   |-- chapter-2.html      # Pagetitle: My Awesome Book. Template: Book_chapters (44)
    |   |-- chapter-3.html      # Idem
    |   |-- chapter-4.html      # etc.
    |   |-- chapter-5.html      
    |   ...
    |   |-- chapter-12.html
    

    The idea is to be able to jump from one chapter page to another using the previous/next links (usually placed just after the content).

    Sot let's get started!

    THE SETUP

    1) Create a new property set for our custom navigation links

    Open getResources from the Elements tab
    Under the properties tab, click on "Add Property Set"
    In the window that pops up, check the "Create new property set" option.
    Give your property set a name. I named mine "FooterNav". Click save.
    This should load your newly created property set. You can now modify the values by right-clicking the property.
    We'll later reference our property set name in our getResources call like this:
    [[getResources@FooterNav? &property=`value`]]
    .
    Go ahead and add/modify the following values: For more details on why we're using these properties, please refer to YJ Tso's in depth coverage here: How-to: Use #MODX getResources for Dynamic Nested Menus
     depth=`0`
     level=`1`
     maxLevel=`2`
     sortby=`menuindex`
     sortdir=`ASC`
     tpl=`myRowTpl`// Used for the next link
     tplFirst=`myFirstRowTpl` // Used for the previous link
     tplWrapper=`myWrapperTpl`
     showHidden=`Yes` // My resources are hidden from the tree. Yours may not be, so this is optional.

    Don't forget to save your property set.

    2) Create the chapter number TV

    I've created a text TV that I've named "Chapter" to store chapter numbers and I've assigned it to my Book_chapters template. You'll see why in a moment.

    3) Create your tpl chunks

    myRowTpl => This chunk will be used as our next link template.

    //Styled with Bootstrap V.3.1.1
    <div class="btn-group pull-right">
     <a href="[[~[[+id]]]]" class="btn btn-default" title="[[+pagetitle]] Chapter [[+tv.Chapter]]" rel="next">[[+pagetitle]] Chapter [[+tv.Chapter]] →</a> 
    [[[[+level:lt=`[[+maxLevel]]`:then=`getResources@FooterNav? &parents=`[[+id]]` &level=`[[+level:add]]``:else=`-`]]]]          
    </div>
    

    myFirstRowTpl => This chunk will be used as our previous link template.

    //Styled with Bootstrap V.3.1.1
    <div class="btn-group pull-left">
     <a href="[[~[[+id]]]]" class="btn btn-default bbl-toolbar-btn" title="[[+pagetitle]] Chapter [[+tv.Chapter]]" rel="prev">← [[+pagetitle]] Chapter [[+tv.Chapter]]</a>
    [[[[+level:lt=`[[+maxLevel]]`:then=`getResources@FooterNav? &parents=`[[+id]]` &level=`[[+level:add]]``:else=`-`]]]]          
    </div>
    

    myWrapperTpl => This chunk will be used to wrap our links. [[+output]] will be replaced with the processed content of our previous/next links chunks.
    //You can add some HTML if you like. 
    [[+output]]

    THE MAGIC PART

    4) Create the (crappy and lazy) snippet that will help determine the previous link
    I named mine getPreviousChap

    /**
     * getPreviousChap
     *
     * DESCRIPTION
     *
     * Helper Snippet that subtracts &y from the supplied number &x 
     *
     * PROPERTIES:
     *
     * &x integer required. Default: 1
     *
     * USAGE:
     *
     * [[!getPreviousChap? &x=`10` &y=`1`]]
     *
     */
    $x = (int) $modx->getOption('x', $scriptProperties, 1);
    $y = (int) $modx->getOption('y', $scriptProperties);
    
    return $x - $y;


    5) Finally, the getResources calls

    <!-- Footer nav-->    
    //Styled with Bootstrap V.3.1.1
    <div class="panel panel-default">
      <div class="panel-body">
        <!-- Previous link -->
        [[*Chapter:gt=`1`:then=`[[getResources@FooterNav? &parents=`927` &limit=`1` &includeTVs=`1` &processTVs=`1` &offset=`[[getPreviousChap? &x=`[[*Chapter]]` &y=`2`]]` &where=`{"template:=":44, "pagetitle:=":"[[*pagetitle]]"}`]]`]]
        <!--/. Previous link -->
    
        <!-- Next link -->
        [[getResources@FooterNav? &parents=`927` &limit=`1` &includeTVs=`1` &processTVs=`1` &offset=`[[*Chapter]]` &where=`{"template:=":44, "pagetitle:=":"[[*pagetitle]]"}`]]
        <!--/. Next link -->
      </div>
    </div>
    <!--/. Footer nav-->
    


    And voilà!

    LET'S EXPLAIN

    Basically the links are generated by two getResources calls. The first one takes care of our previous navigation, while the second brings us forward. The trick is to use the &offset property to determine your previous and next links.

    [[*Chapter:gt=`1`:then=`... => Do not display the previous link on Page one.
    getResources@FooterNav => We're referecing the custom property set we created earlier.
    &tpl, &tplFirst => The &tpl/&tplFirst properties arent' included in the call, because we've already defined them in our @FooterNav property set.
    &limit=`1` => We're limiting our output to 1 to ensure that only one link is displayed
    &where=`{"template:=":44, "pagetitle:=":"[[*pagetitle]]"}` => Only grab resources whose template ID is 44 and page title is equal to the current. Our sample book website happens to be be using the same pagetitle for all its chapter pages. Aliases and [[*Chapter]] are specific to the resources. You can use a TV instead of repeating page titles to identify the resources you want to browse. You would then use &tvFilters=%myTVName% in your call. This is not crucial though.

    The magic trick

    &offset=`[[getPreviousChap? &x=`[[*Chapter]]` &y=`2`]]` => We're subtracting 2 => (&y=`2`) from the active resource's Chapter TV value. This gives us the offset number we need to display the previous link's URL. For example: if the getResources call retrieves 12 results (although only 1 is visible per the &limit property) and the current resource's Chapter TV has a value of 11, by setting the offset property to 9 (as discussed above), we're basically telling getResources to display the 10th result by skipping the first 9 results. And this happens to be our "previous link", relative to the one being browsed.

    I hope I haven't lost you yet.

    &offset=`[[*Chapter]]` => Getting the next resource's URL is much easier - we simply use the active resource's Chapter TV. To use our previous example: if the current resource's Chapter TV has a value of 11 and we set the &offset property to 11, we're telling getResources to display the 12th result by skipping the first 11 results. And that's how we get our "next link", relative to the resource being browsed.

    Conclusion

    The example used in the tutorial was very specific to a project I'm working on, but hopefully you get point. The magic trick is the &offset property.

    I'm definitely not an expert MODXer and I suspect, there's a much better approach. Please feel free to suggest better ways or correct this one wherever appropriate.

    Cheers! [ed. note: treigh last edited this post 12 years, 6 months ago.]
      A MODx Fanatic
      • 30585
      • 833 Posts
      Two months after posting this tutorial, I've realized there's an easier way to achieve the same goal. The following method still uses getResources, but more efficiently this time thanks to runSnippet.

      Let's have a look:
      <?php
      /*
       *@snippet NextChild
       *
       *Helper snippet that displays previous and next links 
       *Requires getResources
       *
       * Basic Usage: -- See optimized usage in the notes below
       *
       *	[[!NextChild? &x=`[[*myOffsetTV]]`]] => will display the next page link
       *	[[!NextChild? &navdir=`left` &tpl=`tplPrevChild` &x=`[[*myOffsetTV]]`]] => will display the previous page link
       */
      
      // set defaults
      $output    = $modx->getOption('default', $scriptProperties, '');
      $parents   = $modx->getOption('parents', $scriptProperties, 927); // Document ID
      $template  = $modx->getOption('template', $scriptProperties, 44); // Template ID
      $tpl       = $modx->getOption('tpl', $scriptProperties, 'tplNextChild');
      //id of the document from which to retrieve the pagetitle. 
      $id        = $modx->getOption('id', $scriptProperties, $modx->resource->get('id'));
      $page      = $modx->getObject('modResource', $id);
      $pagetitle = $page->get('pagetitle');
      //Navigation direction: left or right. Defaults to right => next page link
      $navdir    = $modx->getOption('navdir', $scriptProperties, 'right');
      // Utility property used to determine the offset value. See line 30
      $x         = (int) $modx->getOption('x', $scriptProperties, 1);
      
      //Create the offset value for getResources
      //This is technically how the previous 
      //and next links are generated
      if ($navdir == 'left' && $x <= 1) {
          
          $offset = 0;
          
      } elseif ($navdir == 'left' && $x > 1) {
          
          $offset = $x - 2;
          
      } else {
          $offset = $x;
      }
      
      //getResources options
      $where = array(
          'template:=' => 44,
          'pagetitle:LIKE' => '%' . $pagetitle . '%'
      );
      
      $params = array(
          'parents' => $parents,
          'depth' => 0,
          'showHidden' => 1,
          'where' => $modx->toJson($where),
          'limit' => 1,
          'offset' => $offset,
          'level' => 1, //Custom property
          'max_level' => 2, //Custom property
          'includeTVs' => 1,
          //For perfomarce reasons, let's only get the TVs we need
          'includeTVList' => 'Chapter',
          'sortby' => 'menuindex',
          'sortdir' => 'ASC',
          'sortdirTV' => 'ASC',
          'tpl' => $tpl,
          'tplWrapper' => $tplWrapper
      );
      
      //Run getResources to grab our target pages
      $output = $modx->runSnippet('getResources', $params);
      
      // Return results 
      return $output;

      And that's it!

      No need for a special property set or another snippet just to get chapter numbers (if you're following our children's book example above). Just a single wrapper snippet suffices.

      Let's put it to use:

      The next page link
      [[[[*Chapter:lt=`[[+total]]`:then=`!NextChild? &x=`[[*Chapter]]``]]]]

      This call will display the next page link and also ensure that the link is only visible if the offset (skip) value is smaller than the total number of Resources selected, irrespective of the limit value set in the snippet.

      Why not just do this: [[!NextChild? &x=`[[*myOffsetTV]]`]] you ask? Well imagine our snippet returned a total number of 3 resources (although only one is visible per the limit value), this code: [[!NextChild? &x=`3`]] essentially tells the snippet to skip the first 3 results and display the fourth, which doesn't exist. That's why we need to specify a display condition.

      The optimized tag syntax used is discussed by Opengeek in this blog post: Tags as the Result or How Conditionals are like Mosquitoes

      The previous page link

      For our previous page link, we will need to set the &navdir option to "left", telling our snippet to navigate us leftwards. We will also need to change the &tpl since the default one is designed for eastbound navigation.
      [[[[*Chapter:gt=`1`:then=`!NextChild? &navdir=`left` &tpl=`tplPrevChild` &x=`[[*Chapter]]``]]]]

      Here our snippet will hide the previous link on page 1 (idx=1) and display it on all other pages.

      Our tpl Chunks

      tplNextChild
      <a href="[[~[[+id]]]]" title="[[+pagetitle]] Chapter [[+tv.Chapter]]" rel="next">[[+pagetitle]] Chapter [[+tv.Chapter]] →</a>

      tplPrevChild
      <a href="[[~[[+id]]]]" title="[[+pagetitle]] Chapter [[+tv.Chapter]]" rel="previous">← [[+pagetitle]] Chapter [[+tv.Chapter]]</a>

      As always, please feel free to improve or suggest better ways. Until a better solution is available, I hope someone will find this as useful as I did.
        A MODx Fanatic
        • 37099
        • 338 Posts