Hide and Display menu Core UI

4.1k Views Asked by At

I integrated the template Core UI in my application.

The redirection is configured on _nav.js like presented by that picture:

enter image description here

I'm asking if it's possible to hide or display a menu depending on such condition ?.

For Exemple: Show Public Student and Hide Manage Convention depending on a condition.

enter image description here

The menu is defined on _nav.js

export default [
  {
    _tag: 'CSidebarNavTitle',
    _children: ['Menu'],
  },
  {
    _tag: 'CSidebarNavItem',
    name: 'Public Space',
    to: '/home',
  }, // ...
  {
    _tag: 'CSidebarNavItem',
    name: 'Manage Convention',
    to: '/manageConvention',
  }  // ...
]

Then, this Array is called on TheSidebar.js

import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { CCreateElement, CSidebar, CSidebarBrand, CSidebarNav, CSidebarNavDivider, CSidebarNavTitle, CSidebarMinimizer, CSidebarNavDropdown, CSidebarNavItem } from '@coreui/react'

import CIcon from '@coreui/icons-react'

// sidebar nav config
import navigation from './_nav'

const TheSidebar = () => {
  const dispatch = useDispatch()
  const show = useSelector(state => state.sidebarShow)

  return (
    <CSidebar
      show={show}
      onShowChange={(val) => dispatch({type: 'set', sidebarShow: val })}
    >
      <CSidebarBrand className="d-md-down-none" to="/">
        <CIcon
          className="c-sidebar-brand-full"
          name="logo-negative"
          height={35}
        />
        <CIcon
          className="c-sidebar-brand-minimized"
          name="sygnet"
          height={35}
        />
      </CSidebarBrand>
      <CSidebarNav>

        <CCreateElement
          items={navigation}
          components={{
            CSidebarNavDivider,
            CSidebarNavDropdown,
            CSidebarNavItem,
            CSidebarNavTitle
          }}
        />
      </CSidebarNav>
      <CSidebarMinimizer className="c-d-md-down-none"/>
    </CSidebar>
  )
}

export default React.memo(TheSidebar)

Any suggestion will be appreciated.Big Thanks.

6

There are 6 best solutions below

0
On BEST ANSWER

You can use a child component class SidebarChild in TheSidebar.js to precise the condition that you want.

Then, inject it in <CSidebar >.

and don't forget to use the constant const SBChild = connect()(SidebarChild) to be able to call SBChild inside <CSidebar >.

Hope To Help.

1
On

Reading your comment, you can call an API to check for authorization (preferrably, just after login) and save it to state. You can use it as a flag as I've done below:

Try Changing the true on first line below to false.

const getWorldAccess = () => true;//Call API for user access rights here and save it to a state

const World = () => <div>World</div>;

const Hello = () => {
    return (
      <div>Hello {getWorldAccess() && <World />}</div>
    );//replace getWorldAccess() with a state variable (you don't want to call API infinitely)
}

ReactDOM.render( <
  Hello / > ,
  document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

0
On

In the component, CSidebar just set value of minimize to true

0
On

I think this is the best answer to your question. First, you have to divide items in the _nav.js file according to the below structure. (This allows, to export the navbar items according to user levels).

import React from 'react'
import CIcon from '@coreui/icons-react'

var navStaff = { 
 items: [
    {
      _tag: 'CSidebarNavItem',
      name: 'Dashboard',
      to: '/dashboard',
      icon: <CIcon name="cil-speedometer" customClasses="c-sidebar-nav-icon"/>,
    }
 ]
};

var navAdmin = {
  items: [
    {
      _tag: 'CSidebarNavItem',
      name: 'Dashboard',
      to: '/dashboard',
      icon: <CIcon name="cil-speedometer" customClasses="c-sidebar-nav-icon"/>,
    },
    {
      _tag: 'CSidebarNavTitle',
      _children: ['USERS']
    },
    {
      _tag: 'CSidebarNavItem',
      name: 'Users',
      to: '/users',
      icon: 'cil-people',
    }
  ]
};

export {navStaff, navAdmin };

Then your TheSidebar.js should look like this which is used to allow different content in the navbar. If you want you can use the local storage to allow only the logged user type to allocate the selected navbar.

import React, { lazy, useState, useEffect, useRef} from 'react'
import { useSelector, useDispatch } from 'react-redux'
import {
  CCreateElement,
  CSidebar,
  CSidebarBrand,
  CSidebarNav,
  CSidebarNavDivider,
  CSidebarNavTitle,
  CNavItem,
  CProgress,
  CSidebarMinimizer,
  CSidebarNavDropdown,
  CSidebarNavItem,
} from '@coreui/react'
import CIcon from '@coreui/icons-react'

// sidebar nav config
import {navStaff, navAdmin} from './_nav'

const TheSidebar = () => {

  console.log(navStaff)

  const dispatch = useDispatch()
  const show = useSelector(state => state.sidebarShow);

  return (
    <CSidebar
      show={show}
      unfoldable
      onShowChange={(val) => dispatch({type: 'set', sidebarShow: val })}
    >
      <CSidebarBrand className="d-md-down-none" to="/">
        <CIcon
          className="c-sidebar-brand-minimized"
          name="sygnet"
          height={35}
        />
      </CSidebarBrand>
      <CSidebarNav>

        <CCreateElement
          items={navAdmin.items}
          components={{
            CSidebarNavDivider,
            CSidebarNavDropdown,
            CSidebarNavItem,
            CSidebarNavTitle
          }}
        />

        <CSidebarNavDivider />
      </CSidebarNav>
      <CSidebarMinimizer className="c-d-md-down-none"/>
    </CSidebar>
  )
}

export default React.memo(TheSidebar)

Hope you get the answer which you need. Thanks.

0
On

You can filter the _nav.js array within the TheSideBar.js according to the condition you want. That will hide it from displaying in sidebar. You will have to know what the index of the item you want to remove.

0
On

I know it's an old question but:

I think the simplest solution would be to add a meta prop inside _nav.js to every "CNavItem"

//_nav.js

const _nav = [    
       {
        component: CNavItem,
        name: 'Locations',
        to: '/location',
        meta: { role: ['Admin', 'Recruiter'] },
        icon: <CIcon icon={cilLocationPin} customClassName="nav-icon" />
      },
]

Helper function

export default function hasAccess(userRole, roles) {
    if (Array.isArray(userRole)) {
        return roles.some((r) => userRole.map((item) => item.toLowerCase()).includes(r.toLowerCase()))
    } else {
        return roles.map((item) => item.toLowerCase()).includes(userRole.toLowerCase())
    }
}

After, inside AppSidebarNav.js component, check if the nav item includes at least one of the user role. If it does, render it, otherwise no.

userRole it's returned from redux store in this case. It's up to you how you get the logged in user roles.

//AppSidebarNav.js

export const AppSidebarNav = ({ items }) => {
  const userRole = useSelector((state) => state.user.role)

  {...}

  const navItem = (item, index) => {
    const { component, name, badge, icon, meta, ...rest } = item
    const Component = component
    return (

      hasAccess(userRole, item.meta?.role || []) && <Component

        {...(rest.to &&
          !rest.items && {
          component: NavLink,
        })}
        key={index}
        {...rest}
      >
        {navLink(name, icon, badge)}
      </Component>
    )
  }
 
 
 {...}

  return (
    <React.Fragment>
      {items &&
        items.map((item, index) => (item.items ? (navGroup(item, index)) : navItem(item, index)))}
    </React.Fragment>
  )
}