Nesting of components (get slider content)

Hey,
i have a hero-slider, but i cant manage to get the content of the slides.
I tried a lot of different things but i miss something.
What am i missing?

The structure looks like this:

Slider:

'WG.BaseSite:Content.Hero.Slider':
  superTypes:
    'Neos.Neos:Content': true
    'Neos.Neos:ContentCollection': true
  ui:
    label: 'Hero Slider'
    icon: 'icon-picture'
    group: 'general'
    inspector:
      groups:
        slider:
          label: 'Slider Einstellungen'
  constraints:
    nodeTypes:
      '*': false
      'WG.BaseSite:Content.Slider.Slide': true
  childNodes:
    '':
      type: 'WG.BaseSite:Content.Slider.Slide'

Slides:

'WG.BaseSite:Content.Slider.Slide':
  superTypes:
    'Neos.Neos:Content': true
  ui:
    label: 'Slide'
    icon: 'icon-picture'
    inspector:
      groups:
        image:
          label: 'Bild'
          position: 'start'
        text:
          label: 'Text'
          position: 'end'
  properties:
    image:
      type: Neos\Media\Domain\Model\ImageInterface
      ui:
        label: 'Bild'
        reloadIfChanged: true
        inspector:
          group: 'image'
          position: 10
    text:
      type: string
      ui:
        label: 'Text'
        inspector:
          group: 'text'
          position: 20

Inside the page node, its set as childNode:

'WG.BaseSite:Document.Page':
  superTypes:
    'WG.BaseSite:Document.AbstractPage': true
  ui:
    label: i18n
    icon: icon-file-o
    position: 100
    inspector:
      groups:
        options:
          label: 'options'
          position: 10
  properties:
  childNodes:
    slider:
      type: 'WG.BaseSite:Content.Hero.Slider'
      ui:
        label: 'Slider'
      position: 10
    main:
      type: 'Neos.Neos:ContentCollection'
      ui:
        label: 'Inhalt'
      position: 20

On the fusion site, the structure is like this:

The page is based on the default layout:

prototype(WG.BaseSite:Document.Page) < prototype(WG.BaseSite:Document.AbstractPage) {

    body = WG.BaseSite:Component.DefaultLayout {
        content = ''
        @process.normalize = CodeQ.UnicodeNormalizer:Normalizer
    }

    body.content = Neos.Fusion:Component {
        main = Neos.Neos:ContentCollection {
            nodePath = 'main'
        }

        renderer = afx`
            <main class="page-content" id="main">
                {props.main}
            </main>
        `
    }
}

the default layout includes structure components:

prototype(WG.BaseSite:Component.DefaultLayout) < prototype(Neos.Fusion:Component) {
    content = ''

    renderer = afx`
        <WG.BaseSite:Component.SkipNavigation />
        <WG.BaseSite:Component.TopBar />
        <WG.BaseSite:Component.Header />
        <WG.BaseSite:Component.HeroSection />
        <WG.BaseSite:Component.Breadcrumb />
        {props.content}
        <WG.BaseSite:Component.Footer />
    `
}

Inside the HeroSection, the slider is included.

prototype(WG.BaseSite:Component.HeroSection) < prototype(Neos.Fusion:Component) {

    node = ${node}

    hero = WG.BaseSite:Content.Hero.Slider {
        node = ${q(node).children('slider').get(0)}
        @if.hasSlider = ${q(node).children('slider').count() > 0}
    }


    menuItems = Neos.Neos:MenuItems {
        startingPoint = ${site}
        maximumLevels = 3
    }

    polygonLeft =  Neos.Fusion:Tag {
      tagName = 'img'
      attributes {
        src = Neos.Fusion:ResourceUri {
          path = 'resource://WG.BaseSite/Public/Frontend/images/polygon-left.svg'
        }
      }
    }

    renderer = afx`
        <section class="hero">
            {props.hero}

              <!-- Off-canvas Menu -->
      <div class="off-canvas-wrapper">
        <div class="off-canvas position-right" id="offCanvas" data-off-canvas>
          {props.polygonLeft}
          <ul class="vertical menu">
            <li class="border-below">
                <button class="close-button" aria-label="Close menu" type="button" id="closeOffCanvas">
                <span aria-hidden="true">&times;</span>
              </button>
            </li>
            <Neos.Fusion:Loop items={props.menuItems} itemName="menuItem">
              <li class={menuItem.state == 'current' ? 'active' : ''}>
                <Carbon.Link:Link
                  node={menuItem.node}
                  backendLink={true}
                  renderDefaultTagIfNoLink={true}>
                  <span>{String.toUpperCase(menuItem.label)}</span>
                </Carbon.Link:Link>
              </li>
            </Neos.Fusion:Loop>
          </ul>

        </div>
      </div>
        </section>

    `
}

The slider fusion looks like this:

prototype(WG.BaseSite:Content.Hero.Slider) < prototype(Neos.Neos:ContentComponent) {

    node = ${node}

    slides = Neos.Fusion:Collection {
            collection = ${q(node).children().get()}
            itemName = 'node'
            itemRenderer = WG.BaseSite:Content.Slider.Slide {
                node = ${node}
            }
        }

    renderer = afx`
        <section class="page-intro">
            <div class="slider-wrapper">
                <div class="orbit" role="region" aria-label="" data-orbit data-use-m-u-i="false">
                    <div class="orbit-wrapper">
                        <ul class="orbit-container">
                            {props.slides}
                        </ul>
                    </div>
                </div>
            </div>
        </section>
    `
}

And the slide fusion looks like this:

prototype(WG.BaseSite:Content.Slider.Slide) < prototype(Neos.Fusion:Component) {
    renderFrontend = ${site.context.inBackend ? false : true}
    node = ${node}

    image = ${q(node).property('image')}
    imageUri = Neos.Neos:ImageUri {
        asset = ${q(node).property('image')}
        width = 1920
        height = 1080
        allowCropping = true
        allowUpscaling = true
    }

    text = Neos.Neos:Editable {
        property = 'text'
    }

    renderer = afx`
        <li class="orbit-slide">
            <figure class="orbit-figure">
                <Neos.Fusion:Tag
                    @if.hasImage={props.image}
                    tagName="img"
                    attributes={{
                        src: props.imageUri,
                        alt: ${q(node).property('title') || 'Slider image'},
                        class: 'orbit-image'
                    }}
                />
                <figcaption class="orbit-caption">
                    <div class="grid-container">
                        <div class="grid-x">
                            <div class="cell small-6 medium-4">
                                {props.text}
                            </div>
                        </div>
                    </div>
                </figcaption>
            </figure>
        </li>
    `
}

Hi @BoxerBuffa,

as far as I see, the way you pass the node from WG.BaseSite:Component.HeroSection to WG.BaseSite:Content.Hero.Slider does not work.

You pass it as a “local” property node to WG.BaseSite:Content.Hero.Slider

    hero = WG.BaseSite:Content.Hero.Slider {
        node = ${q(node).children('slider').get(0)}
        @if.hasSlider = ${q(node).children('slider').count() > 0}
    }

but within, WG.BaseSite:Content.Hero.Slider you try to access node from the current context ${q(node).children().get()}. But this might be you page node at this point (I assume). You need to put the new node into the context, to make it accessable within the slides collection.

prototype(WG.BaseSite:Content.Hero.Slider) < prototype(Neos.Neos:ContentComponent) {

    node = ${node}
    @context.node = ${this.node}

    slides = Neos.Fusion:Collection {
            collection = ${q(node).children().get()}
            itemName = 'node'
            itemRenderer = WG.BaseSite:Content.Slider.Slide {
                node = ${node}
            }
...

The Neos.Fusion:Collection is btw. deprecated and if you want proper UI integration you can simply use Neos.Neos:ContentCollectionRenderer without any additional props, as it will automatically render the current nodes (node set via @context or itemName in the loop) children with their matching prototypes assuming.

To prevent invalid markup when adding items you need to add the attribute

data-__neos-insertion-anchor = true
data-__neos-insertion-anchor.@if.onlyRenderInBackend = ${node.context.inBackend && node.context.currentRenderingMode.edit}

to the tag that wraps the items, so the UI knows where to put new elements.

1 Like

Thanks for the hint, where do i find documentation about the Neos.Neos:ContentCollectionRenderer? Cant find it in web or i am searching wrong.

Thanks for helping.

I understand the problem at this point, but its strange, if i add
@context.node = ${this.node}
to the slider template like:

prototype(WG.BaseSite:Content.Hero.Slider) < prototype(Neos.Neos:ContentComponent) {

    node = ${node}
    @context.node = ${this.node}

    slides = Neos.Fusion:Collection {
            collection = ${q(node).children().get()}
            itemName = 'node'
            itemRenderer = WG.BaseSite:Content.Slider.Slide {
                node = ${node}
            }

The slider is not longer rendered at all. Like the condition in the HeroSection is false now.

  hero = WG.BaseSite:Content.Hero.Slider {
        node = ${q(node).children('slider').get(0)}
        @if.hasSlider = ${q(node).children('slider').count() > 0}
    }

if i remove it, i have two slides rendered, but nut the content (text and image) of it, just the static html part.

very strange but i cant find a solution with my little knowlage i have of neos so far.

It’s hard to say, just seeing the prototypes here. I’d try to go that step by step and check where the issue starts. First of all you could try to rename the node within your Hero.Slider to something unique to prevent issues with the outer node.

Remove the conditions, go step by step. So first get the outer slider working, then debug the query to get the children.

Btw. you can simply use a ContentCollection without any overrides to render them.