Previous <---> Next navigation


(Hugh McMaster) #1

Squiz Matrix v5.4.3.1

Hi everyone,

I’m trying to implement basic navigation to move forwards or backwards via the asset tree.

Currently, the page structure is in three sections:

  1. An ordered list of pages in the section (from 1 to n… ). This list is dynamically generated using a nested asset listing in a parent asset listing (with list_current_asset_id).
  2. The frontend page contents
  3. Previous <—> Next navigation

I’m struggling to implement the navigation aspect. %asset_sibling_prev% and %asset_sibling_next% have reasonable functionality, but produce unwanted pages, e.g. pages under construction or even the parent asset’s content container, when I actually want the previous live page. Nor do these keywords seem to respect asset status settings in an asset listing.

I’m currently looking at outputting a list and using jQuery to remove unwanted elements, but these seems more of hack than anything else.

What other methods are commonly used to dynamically produce this sort of navigation? I’d like to implement a solution like the one used on the Squiz manual, e.g. https://matrix.squiz.net/manuals/other-cms-assets/chapters/multiple-page


Multi-page Template Design
(Bart Banda) #2

If you have certain rules around whether to print the sibling asset or not, you might be able to do it best with some SSJS.

So first get the list of all siblings at the same level using %asset_parent^as_asset:asset_children% (maybe use %asset_children_link_type_1% if you only want type 1 links. This will also filter out any assets that the user doesn’t have read access to, such as UC assets.

Feed that into a JS function that figures out the next and previous asset ID based on the current asset ID being viewed on the frontend, and then do something like:

print('%'+ 'globals_asset_name_linked:'+ prevAssetID +'%');
print('%'+ 'globals_asset_name_linked:'+ nextAssetID +'%');

(Hugh McMaster) #3

Bart,

You are a genius! SSJS works perfectly for producing the required Previous <–> Next functionality.

I did modify your solution slightly – to include the parent asset ID in the array – but apart from that, I followed your solution as written.

Your solution was far simpler than I had anticipated, and, indeed, had attempted myself. Knowing how to output the sibling array was the vital piece of information I needed.

Thank you

Hugh


(Michael Wilson) #4

I’m looking at implementing the same navigation Hugh. I was wondering if you might be able to share your solution in a bit more detail. I’d like to provide prev/next buttons so users can navigate sequentially through our annual report section which also has sub-sections down to three levels.


(Hugh McMaster) #5

Hi Michael,

My use case was for a parent and one sub-level, but I expect the solution to scale across multiple levels.

The solution uses two standard page assets: Prev-Next Overview and Prev-Next Siblings. The former is nested in the top-level page, and the latter is nested in the child pages.

To get the first child of the parent page, I get an array of child assets (Type 2, in this case) and keep the first item in the array. Then I print it using the %globals_asset_name_linked:nnnnn% keyword.

<script runat="server">
    var list = %frontend_asset_children_link_type_2^array_slice:1:1%;
    var nextAssetID = list[0];

    print('<div id="prev-next-nav">');
    print('<p class="float-right">Next: %globals_asset_name_linked:' + nextAssetID +'%</p>');
    print('</div>');
</script>

Getting the the sibling navigation is a bit more complicated. This time, we get an array of sibling assets (the children of the frontend asset’s parent) and remove the first item (the parent asset’s Page Contents container). Then we set up some variables and add the ID of the frontend parent asset to the start of the array.

Iterating through the array, we check if the current item is the frontend asset ID. If it is, we get the previous asset ID, since it always exists (remember, we added the frontend parent asset ID to the array, so we won’t go out of bounds).

To get the next sibling asset, we need to check our position in the array. Remember, we need to allow for the current frontend asset being the final sibling asset, which is why we use list.length - 1.

After this, we just need to print the hyperlink, checking for a null value, in case we are on the final sibling asset.

<script runat="server">
    var list = %frontend_asset_parent^as_asset:asset_children_link_type_2^array_slice:1%;
    var currentAssetID = %frontend_asset_assetid%, prevAssetID, nextAssetID;
    list.unshift(%frontend_asset_parent%);

    for (i = 1; i < list.length; i++) {
        if (list[i] == currentAssetID) {
            prevAssetID = list[i-1];
            nextAssetID = (i < list.length - 1) ? list[i+1] : null;
            break;
        }
    }
    print('<div id="prev-next-nav">');
    if (prevAssetID) {
        print('<p class="float-left">Previous: %globals_asset_name_linked:' + prevAssetID + '%</p>');
    }
    if (nextAssetID) {
        print('<p class="float-right">Next: %globals_asset_name_linked:' + nextAssetID + '%</p>');
    }
    print('</div>');
</script>

Happy to help if you have any questions.


(Michael Wilson) #6

Late last year we did actually release this functionality onto our site, but I forgot to post the solution here. With the help of Hugh’s code I created a paint layout containing the below code which allows the user to navigate through the section in a linear fashion. The result can be seen on our Annual Report page if you select “Read Online”.

getNextAssetID and getPreviousAssetID are the main functions with the other functions supporting these and making the code a bit more logical to read. If you have ideas on how to make it better feel free to post them here. I really should have included a way to identify the root node of the section this is used on so I could hide the previous or next link from the first asset and last asset respectively.

<script runat="server">
    
    function CurrentAsset() {
        this.ID = %frontend_asset_assetid%;
        this.Children = %frontend_asset_children_link_type_1^empty:[]%;
        this.NextSibling = %asset_sibling_next_type_1^empty:null%;
        this.ParentID = %frontend_asset_parent%;
        this.ParentsNextSiblingID = %frontend_asset_parent^as_asset:asset_sibling_next_type_1^empty:null%;
        
        this.HasChildren = this.Children.length > 0? true : false;
    }
    CurrentAsset.prototype.getFirstChildID = function() {
        return this.HasChildren ? this.Children[0] : null;
    }
    CurrentAsset.prototype.getLastChildID = function() {
        return this.HasChildren ? this.Children[this.Children.length - 1] : null;
    }
    CurrentAsset.prototype.isFirstChild = function() {
        var siblings = getSiblings();
        var siblingPosition = getSiblingPosition(siblings, this.ID);
        
        return siblingPosition === 0 ? true : false;
    }
    CurrentAsset.prototype.isLastChild = function() {
        var siblings = getSiblings();
        var siblingPosition = getSiblingPosition(siblings, this.ID);
        
        return siblingPosition === siblings.length - 1 ? true : false;
    }
    CurrentAsset.prototype.isParentLastChild = function() {
        var currentAssetGrandparentChildren = %frontend_asset_parent^as_asset:asset_parent^as_asset:asset_children_link_type_1%;
        var currentAssetParentPosition = currentAssetGrandparentChildren.indexOf(this.ParentID.toString());

        
        return currentAssetParentPosition === currentAssetGrandparentChildren.length - 1 ? true : false;
    }
                 
    function getLinkToAsset(assetID, direction) {
        if (assetID) {
            return direction === 'Previous' ? '<a id="hl' + direction + '" href="%globals_asset_url:'+ assetID +'%"><i class="fa"></i>%globals_asset_name:' + assetID + '%</a>' : 
                '<a id="hl' + direction + '" href="%globals_asset_url:'+ assetID +'%">%globals_asset_name:' + assetID + '%<i class="fa"></i></a>'
        } else {
            return '<span>&nbsp; </span>';
        }
    }
    function getSiblingPosition(siblings, id) {
        return siblings.indexOf(id.toString());
    }
    function getSiblings() {
        return %frontend_asset_parent^as_asset:asset_children_link_type_1%
    }
    function getNextAssetID() {
        
        var currentAsset = new CurrentAsset();
        
        if (currentAsset.HasChildren)
            return currentAsset.getFirstChildID();
        
        if (!currentAsset.isLastChild())
            return currentAsset.NextSibling;

        if (!currentAsset.isParentLastChild())
            return currentAsset.ParentsNextSiblingID;
        
        return null;
    }
    function getPreviousAssetID() {
        var currentAsset = new CurrentAsset();
        
        if (currentAsset.isFirstChild())
            return currentAsset.ParentID;
        
        var previousSiblingChildren = %asset_sibling_prev_type_1^as_asset:asset_children_link_type_1^empty:[]%;
        if (previousSiblingChildren.length > 1) {
            return previousSiblingChildren[previousSiblingChildren.length - 1];
        }
        
        return %asset_sibling_prev_type_1%;
        
    }
    
    var previousAssetID = getPreviousAssetID(),
        nextAssetID = getNextAssetID();
    
    print(getLinkToAsset(previousAssetID, 'Previous'));
    print(getLinkToAsset(nextAssetID, 'Next'));
    
</script>