Conditional rendering of group field

Hi everyone,

I've built out a navigation model according to this tutorial:

I want to give my content editors the ability to create top level links that have sub links in a dropdown, but also to create top level links that do not. What I'm needing to do is only render my dropdown if there are fields present under the top link. Is that possible, and how could I go about it?

Jesse

Hey Jesse,

This would be done with an if statement at the project level. What technology are you building your app in? Do you have anything implemented so far?

Thanks.

So sorry, I’m building it in Gatsby! At this point all I have implemented is logic to keep the site from breaking if there are no subfields. Here’s what I’ve got:

{slice.fields.map((secondaryNav, index) => {
  if (!slice.fields) {
    return null;
  }
  <!-- markup is rendered here -->
 }

This looks over my slices and as long as the navigation item has sub fields it renders the content.

Hi there everyone! Any thoughts on this?

Thanks!

Shouldn’t that be

{
  if (!slice.fields) {
    return null;
  } else {
    return  slice.fields.map((secondaryNav, index) => {
    <!-- markup is rendered here -->
    })
  }
}

I have also followed the tutorial and to make rendering the markup easier I preprocess the result of the navigation to create a nested structure

const items = content.body.reduce((acc, el) => {
    if (acc.length === 0) {
      return [el];
    }
    const current = acc.pop();
    if (current.slice_type === el.slice_type) {
      acc.push(current);
      acc.push(el);
    } else {
      acc.push({ ...current, items: [...current.items, el] });
    }
    return acc;
  }, []);

this way I go from a flat list of navigation items which declare a nesting level to a nested list of lists of navigation items

so

[ 
  {level_1, ...item1},
  {level_2, ...item1},
  {level_2, ...item2}, 
  {level_1, ...item2},
  {level_2, ...item1},
  {level_2, ...item2},...
]

becomes

{
  {
    level_1, 
    items:[
     {level_2, ...item1},
     {level_2, ...item2}, 
    ],
    ...item1
  },
  {
    level_1, 
    items:[
      {level_2, ...item1},
      {level_2, ...item2}, 
    ],
     ...item2
  }, 
   ...
]
1 Like

Thank you! For the first point, that doesn't work; it doesn't render the dropdown for any of the slices using that.

Secondly, can you show me how you're including this code to render the nested lists?

Our code is a bit more involved and the actual rendering to html will depend quite strongly on the templating framework. in react it looks like this;

const Nav = ({ items }) => (
  <ul>
    {items.map(item => (
      <li>
        {item.text}
        {item.items ? <Nav items={item.items} /> : null}
      </li>
    ))}
  </ul>
);

const Header = ({ content }) => {
  const items = content.body.reduce((acc, el) => {
    if (acc.length === 0) {
      return [el];
    }
    const current = acc.pop();
    if (current.slice_type === el.slice_type || el.slice_type === 'action') {
      acc.push(current);
      acc.push(el);
    } else {
      acc.push({ ...current, items: [...current.items, el] });
    }
    return acc;
  }, []);
  return (
    <nav>
      <Nav items={items} />
    </nav>
  );
};

where content has been bound to the data field of the payload you receive from prismic...

I ended up figuring this out, borrowing some code from here. Here’s the full code for my MainNav component:

export const MainNav = ({ menuItems }: MenuProps) => {
  const navItems = menuItems.main_nav
    ? menuItems.main_nav.map((slice: any, index: any) => {
        const hasDropdown = null !== slice.fields ? slice.fields.length: false;
        if (slice.type === 'nav_item') {
          if (slice.primary.link._linkType === 'Link.document') {
            return (
              <li
              className={s.navigation__item}
              >
                <Link
                  to={linkResolver(slice.primary.link._meta)}
                  className={s.navigation__link}
                  activeClassName={s.navigation__link__active}
                >
                  {RichText.asText(slice.primary.label)}
                </Link>
                { hasDropdown ? (
											<Dropdown slice={slice}></Dropdown>
										) : null }
              </li>
            );
          } else if (slice.primary.link._linkType === 'Link.web') {
            return (
              <li className={s.navigation__item} key={`${index}`}>
                <a href={slice.primary.link.url} target="_blank" className={s.navigation__link}>
                  {RichText.asText(slice.primary.label)}
                </a>
              </li>
            );
          } else {
            return null;
          }
        }
        return null;
      })
    : null;

  return <ul className={s.navigation__menu}>{navItems}</ul>;
};

In here I’m creating a const called hasDropdown that looks through the slice to see if it has any fields:

const hasDropdown = null !== slice.fields ? slice.fields.length: false;

Then when I’m rendering the nav item, I add this conditional:

{ hasDropdown ? (
    <Dropdown slice={slice}></Dropdown>
) : null }

Using this it’s only rendering the Dropdown component for links that have child items.

2 Likes