[Vuejs]-Array manipulation with multiple conditions – Vue.js / JavaScript

1πŸ‘

βœ…

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>

1πŸ‘

in the code snippet below I used a filter first to filter by activatedModules then used a forEach to filter each object children property by userPermissions, I think you can implement this in your vue component or get an idea about how to tackle the problem (hope this helps):

const pages = [{
    text: 'Team',
    moduleID: 'm1',
    children: [{
      text: 'Dashboard',
      route: 'team/dashboard',
      permissions: 'p1382',
      moduleID: 'm1-1'
    }, ],
  },
  {
    text: 'Planner',
    moduleID: 'm2',
    children: [{
        text: 'Events',
        route: '/planner/events',
        permissions: 'p47289',
        moduleID: 'm2-1'
      },
      {
        text: 'Calendar',
        route: '/planner/calendar',
        permissions: 'p283',
        moduleID: 'm2-2'
      },
    ],
  },
  {
    text: 'HR',
    moduleID: 'm3',
    children: [{
        text: 'Staff',
        route: '/hr/staff',
        permissions: 'p34729',
        moduleID: 'm3-1'
      },
      {
        text: 'Config',
        route: '/hr/config',
        permissions: 'p382',
        moduleID: 'm3-2'
      },
    ],
  },
  {
    text: 'Admin',
    moduleID: 'm4',
    children: [{
        text: 'Users',
        route: '/admin/users',
        permissions: 'p3z4',
        moduleID: 'm4-1'
      },
      {
        text: 'Security',
        route: '/admin/security',
        permissions: 'p2u3',
        moduleID: 'm4-2'
      },
    ],
  },
  {
    text: 'Support',
    route: '/support',
    permissions: 'p332j',
    moduleID: 'm5'
  },
];
const activatedModules = ['m1', 'm3', 'm4', 'm5'];
const userPermissions = ['m1-1', 'm3-1', 'm3-2', 'm4-2'];
// This is the source for my navigation drawer:
let filteredPages = null;

filteredPages = pages.filter(x => activatedModules.includes(x.moduleID));
filteredPages.forEach(x => {
 if (x.children)
  x.children = x.children.filter(y => userPermissions.includes(y.moduleID));
});

console.log(filteredPages);

1πŸ‘

Some remarks:

  • You should not be using function for callbacks, like you started doing for filter, as that will make you lose the right this value. Use arrow functions.
  • filter cannot do the job on its own, as you need to also generate new objects which may have fewer children. So you should first map to make those narrowed down objects, and then filter.

Without using Vue, you can run the following snippet, which just hard-codes the call to filterArray:

let app = {
    computed: {
        filterArray() {
            this.filteredPages = this.pages.map(item => {
                let children = (item.children || []).filter(child => 
                    this.activatedModules.includes(child.moduleID) &&
                    this.userPermissions.includes(child.permissions)
                );
                return (children.length || !item.children) 
                    && this.activatedModules.includes(item.moduleID)
                    && {...item, children};
            }).filter(Boolean);
        }
    },
    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'],
        filteredPages: []
    }),
};

// Demo, simulate Vue's call to computed.filterArray
let data = app.data();
app.computed.filterArray.call(data);
// Verify output:
console.log(data.filteredPages);

Leave a comment