Манипуляции с массивами с несколькими условиями — Vue.js/JavaScript

В моем приложении Vue.js я использую панель навигации для отображения различных страниц, к которым у пользователя есть доступ. Страницы также видны только в том случае, если администратор активировал соответствующий модуль. Поэтому для каждой страницы и дочерних элементов устанавливается уникальный moduleID. Список заполнен filteredPages[]. Этот массив является результатом отображения только тех страниц, к которым у пользователя есть доступ. Все доступные страницы хранятся в моем исходном источнике данных pages[].

Подводя итог: страница отображается только в том случае, если выполняются оба этих условия:

  1. activatedModules[] содержит moduleID страницы и дочерние элементы.
  2. userPermissions[] содержит значение permissions дочерних элементов (или страницы, если дочерних элементов нет).

Мой код:

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;
            })
        }
    }
}

Для приведенного выше кода это должно быть выводом:

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' },
            ]

Разрешения пользователя хранятся в Firebase Cloud Firestore следующим образом:

Можете ли вы помочь с фильтрацией массива?

Можете ли вы добавить, что filterArray() должен вернуть в этом конкретном случае?

trincot 24.12.2020 14:44

@trincot В этом случае я обновил вопрос с выводом.

mcd 24.12.2020 15:06

@tao Мое приложение Vue.js довольно сложное и почти готово. Поэтому у меня, вероятно, есть какие-то знания, чтобы хотя бы довести проект до этой стадии. Страницы были закодированы без разрешений ранее. Раньше я манипулировал массивами с помощью filter(), но не с такой сложностью, и не нашел ресурсов в Интернете, которые помогли бы мне правильно решить эту проблему.

mcd 24.12.2020 15:12

В вашем примере есть проблема. p301 есть в разрешениях, но вы комментируете в выводе, что это не так. А для p401 обратная ситуация. Где-то опечатка?

trincot 24.12.2020 15:25

На самом деле, я только что понял ошибку, и она намного глупее, чем я думал: вы по ошибке написали created вместо computed. Я удалю свои предыдущие комментарии.

tao 24.12.2020 15:34

@trincot Да, ты прав. Это опечатка в вопросе. Я исправил это, спасибо.

mcd 24.12.2020 15:37

@tao Спасибо, что указали на это. Так должно было быть created. В моем файле .vue есть больше кода, который не имеет отношения к этой теме. Копируя и вырезая код в SO, я вырезал код перед открытием created. Вы, наверное, все равно в это не поверите. Давайте просто прекратим эту дискуссию, пожалуйста, и лучше используем наше драгоценное время. С Рождеством :)

mcd 24.12.2020 15:42

На самом деле не уверен, почему вы сказали, что я не поверю тому, что я сказал, я понял, прежде чем вы это подтвердили. Поставьте себя на мое место и попытайтесь помочь. Прежде чем понять, что вы заменили computed на created, казалось, что вы не понимаете основ. Самое полезное, что я мог сделать, это побудить вас пройтись по основам. По моему мнению, попытка использовать Vue без базового понимания JavaScript, скорее всего, разочарует.

tao 24.12.2020 16:01
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
8
341
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

в приведенном ниже фрагменте кода я сначала использовал фильтр для фильтрации по activatedModules, а затем использовал forEach для фильтрации каждого свойства children объекта по userPermissions, я думаю, вы можете реализовать это в своем компоненте vue или получить представление о том, как решить проблему (надеюсь, это помогает):

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.info(filteredPages);

Та же проблема, что и в ответе Тао: кажется, есть проблема, когда permissions полностью пуст. Пользователь по-прежнему может видеть страницы верхнего уровня (без дочерних), потому что код просто выводит страницы верхнего уровня.

mcd 25.12.2020 01:19

Некоторые замечания:

  • Вы не должны использовать function для обратных вызовов, как вы начали делать для filter, так как это приведет к потере правильного значения this. Используйте стрелочные функции.
  • filter не может выполнять эту работу сам по себе, так как вам также нужно генерировать новые объекты, у которых может быть меньше потомков. Итак, вы должны сначала map сделать эти суженные объекты, а затем filter.

Без использования Vue вы можете запустить следующий фрагмент кода, который просто жестко кодирует вызов 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.info(data.filteredPages);
Ответ принят как подходящий

Это должно сделать это:

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)
        ))
    );
  }
}

Смотрите, как это работает:

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>

Спасибо, мне очень нравится подход. Однако также возникает проблема, когда permissions полностью пуст. Пользователь по-прежнему может видеть страницы верхнего уровня (без дочерних), потому что код просто выводит страницы верхнего уровня.

mcd 25.12.2020 01:02

Это точно. Спасибо :)

mcd 25.12.2020 13:19

Другие вопросы по теме