Skip to content

Array manipulation with multiple conditions – Vue.js / JavaScript

In my Vue.js application I use a navigation drawer to display the different pages a user has access to. Pages are also only visible if the administrator has activated the related module. Therefore the unique moduleID is set for each page and children. The list is populated by filteredPages[]. This array is the result of displaying only the pages a user has access to. All available pages are stored in my original data source pages[].

To sum this up: A page is only shown if both of these conditions are true:

  1. activatedModules[] contains the moduleID of a page and the children.
  2. userPermissions[] contains the permissions value of a children (or page if there is no children).

My code:

export default {
    data: () => ({
        pages: [
            {
                text: 'Team', moduleID: 'm1',
                children: [
                { text: 'Dashboard', route:'team/dashboard', permissions: 'p101', moduleID: 'm1-1' },
                ],
            },
            {
                text: 'Planner', moduleID: 'm2',
                children: [
                { text: 'Events', route:'/planner/events', permissions: 'p201', moduleID: 'm2-1' },
                { text: 'Calendar', route:'/planner/calendar', permissions: 'p202', moduleID: 'm2-2' },
                ],
            },
            {
                text: 'HR', moduleID: 'm3',
                children: [
                { text: 'Staff', route:'/hr/staff', permissions: 'p301', moduleID: 'm3-1' },
                { text: 'Config', route:'/hr/config', permissions: 'p302', moduleID: 'm3-2' },
                ],
            },
            {
                text: 'Admin', moduleID: 'm4',
                children: [
                { text: 'Users', route:'/admin/users', permissions: 'p401', moduleID: 'm4-1' },
                { text: 'Security', route:'/admin/security', permissions: 'p402', moduleID: 'm4-2' },
                ],
            },
            { text: 'Support', route:'/support', permissions: 'p50', moduleID: 'm5' },
        ],
        activatedModules: ['m1', 'm1-1', 'm3', 'm3-1', 'm3-2' 'm4', 'm4-1', 'm4-2', 'm5'],
        userPermissions: ['p101', 'p301', 'p302', 'p402', 'p50'],
        // This is the source for my navigation drawer:
        filteredPages: []
    }),
    computed: {
        filterArray() {
            // I tried to use filter() but how can I solve the rest?
            this.filteredPages = this.pages.filter(function(item) {
              for (var this.activatedModules in filter) {
                if /* I would assume that I have to write the condition here */
                return false;
              }
              return true;
            })
        }
    }
}

For the code above this should be the output:

filteredPages: [
                {
                    text: 'Team', moduleID: 'm1',
                    children: [
                        { text: 'Dashboard', route:'team/dashboard', permissions: 'p', moduleID: 'm1-1' },
                    ],
                },
                // Notice that 'm2' is missing here because it is not in activatedModules[]
                {
                    text: 'HR', moduleID: 'm3',
                    children: [
                        { text: 'Staff', route:'/hr/staff', permissions: 'p301', moduleID: 'm3-1' },
                        { text: 'Config', route:'/hr/config', permissions: 'p302', moduleID: 'm3-2' },
                    ],
                },
                {
                    text: 'Admin', moduleID: 'm4',
                    children: [
                        // 'm4-1' is in activatedModules[] but the user doesn't have the permission 'p401' to view this
                        { text: 'Security', route:'/admin/security', permissions: 'p402', moduleID: 'm4-2' },
                    ],
                },
                { text: 'Support', route:'/support', permissions: 'p50', moduleID: 'm5' },
            ]

The permissions of a user are stored in Firebase Cloud Firestore like this:

Screenshot of the data model

Can you help with the filtering of the array?

Answer

This should do it:

computed: {
  filteredPages() {
    return this.pages.map(page => ({
      ...page, 
      children: page.children
        // when children is truthy
        ? page.children.filter(
          // filter out those not in `userPermissions`
          child => this.userPermissions.includes(child.permissions)
          // and those not in `activatedModules`
            && this.activatedModules.includes(child.moduleID)
        )
        : page.children
    })).filter(
      // only keep page if in `activatedModules` and...
      page => (this.activatedModules.includes(page.moduleID)) &&
        // if children is truthy and has length or...
        (page.children?.length || (
          // if children is falsy and page.permissions in userPermissions
          !page.children && this.userPermissions.includes(page.permissions)
        ))
    );
  }
}

See it working:

Vue.config.devtools = false;
Vue.config.productionTip = false;

new Vue({
  el: '#app',
  data: () => ({
    pages: [
      {
        text: 'Team',
        moduleID: 'm1',
        children: [
          { text: 'Dashboard', route:'team/dashboard', permissions: 'p101', moduleID: 'm1-1' }
        ],
      }, {
        text: 'Planner',
        moduleID: 'm2',
        children: [
          { text: 'Events', route:'/planner/events', permissions: 'p201', moduleID: 'm2-1' },
          { text: 'Calendar', route:'/planner/calendar', permissions: 'p202', moduleID: 'm2-2' },
        ],
      }, {
        text: 'HR',
        moduleID: 'm3',
        children: [
          { text: 'Staff', route:'/hr/staff', permissions: 'p301', moduleID: 'm3-1' },
          { text: 'Config', route:'/hr/config', permissions: 'p302', moduleID: 'm3-2' },
        ],
      }, {
        text: 'Admin',
        moduleID: 'm4',
        children: [
          { text: 'Users', route:'/admin/users', permissions: 'p401', moduleID: 'm4-1' },
          { text: 'Security', route:'/admin/security', permissions: 'p402', moduleID: 'm4-2' },
        ],
      },
      { text: 'Support', route:'/support', permissions: 'p50', moduleID: 'm5' }
    ],
    activatedModules: ['m1', 'm1-1', 'm3', 'm3-1', 'm3-2', 'm4', 'm4-1', 'm4-2', 'm5'],
    userPermissions: ['p101', 'p301', 'p302', 'p402', 'p50']
  }),
  computed: {
    filteredPages() {
      return this.pages.map(page => ({
        ...page, 
        children: page.children
          ? page.children.filter(
            child => this.userPermissions.includes(child.permissions)
              && this.activatedModules.includes(child.moduleID)
          )
          : page.children
      })).filter(
        page => (this.activatedModules.includes(page.moduleID))
          && (page.children?.length || (
            !page.children && this.userPermissions.includes(page.permissions)
          ))
      );
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <pre v-html="filteredPages" />
</div>