Rendering parts of a page based on visibility of a referenced node

Hey there!

I’m relatively new to Neos and having a bit of trouble with a component I’m building and wonder if anyone could give me a hint.

So I built a Banner component that’s basically a styled ContentCollection [0]

The headerBanner reference property I’ve added to the page’s configuration allows us to add a banner via the inspector. Every page may also have a headerImage, which is supposed to be displayed if no Banner is referenced or the banner is hidden. [1]

Hiding the Banner works just fine, it always correctly toggles isBannerActive. However, if I unhide a banner, isBannerActive isn’t toggled (and I assume the page isn’t re-rendered). Updating any (text) node on the page will trigger the re-rendering, hide the image and display the banner.

I have been tinkering with the cache configuration but without any success and I’ve run out of ideas. :confused:

[0] - Banner component renderer

    renderer = afx`
    <div class={props.bannerClasses} style={props.bannerStyles}>
        <Neos.Neos:ContentCollection attributes={props.contentCollectionAttributes}/>
    </div>
`  

[1] - The current implementation

      headerImage = Neos.Fusion:Case { ... }
      headerBanner = Neos.Fusion:Case { ... }
      isBannerActive = ${q(node).property('headerBanner') ? q(q(q(node).property('headerBanner')).get()).property('_hidden') ? 0 : 1 : 0}

      content = afx`
        <div @if.showHeaderImage={props.headerImage && !props.isBannerActive} class={props.classes} style={props.backgroundImage}>
            <div class="container">

                <h1 class="text-left">{props.headerTitle}</h1>
            </div>
        </div>
        <div @if.showHeaderBanner={props.isBannerActive}>
            {props.headerBanner}
        </div>
         ...`

Hi Florian and welcome to our community!

This is what I call an excellent way to post a question, with brief but thorough and code snippets. Big up! :wink:

From the rest of your description it sounds as though there is only ever one header banner or image. Is a ContentCollection a good fit here?

I just stuttered when I tried to read this :slight_smile:

You could write the same as

isBannerActive = ${!q(q(node).property('headerBanner')).property('_hidden')}

but that’s still slightly confusing to read.
I would suggest you put the headerBanner to the context of your component then you can write

isBannerActive = ${!q(headerBanner).property('_hidden')}

Also note that _hidden is an internal property. Nodes that are hidden are basically non-existent in the Frontend, i.e. q(node).property('headerBanner') will be null if the referenced node is hidden (In the Neos Backend it will still be found, so I assume that you added that condition for the backend rendering)

Maybe try with

prototype(Your.Package:Your.ContentComponent) < prototype(Neos.Neos:ContentComponent) {
    @context {
        headerBanner = ${q(node).property('headerBanner')}
    }
    @cache  {
        mode = 'cached'
        entryIdentifier {
            node = ${node}
        }
        entryTags {
            node = ${Neos.Caching.nodeTag(node)}
            headerBanner = ${Neos.Caching.nodeTag(headerBanner)}
        }
    }
    renderer = ...
}

(untested)

You have to add .context({‘invisibleContentShown’: true}) in the context to get a correct caching entry also when the node gets hidden (otherwise headerBanner would be null). Than you get the element back after reactivating the headerBanner:

prototype(Your.Package:Your.ContentComponent) < prototype(Neos.Neos:ContentComponent) {
        @context {
            headerBanner = ${q(node).context({'invisibleContentShown': true}).property('headerBanner')}
        }
        @cache  {
            mode = 'cached'
            entryIdentifier {
                node = ${node}
            }
            entryTags {
                node = ${Neos.Caching.nodeTag(node)}
                headerBanner = ${Neos.Caching.nodeTag(headerBanner)}
            }
        }
        renderer = ...
    }