Vue фильтрация всех объектов в массиве, даже если они не соответствуют критериям поиска - PullRequest
1 голос
/ 06 апреля 2020

Я строю глоссарий в Vue и добавляю вычисляемый фильтр. Он фильтрует результаты, но возвращает все объекты в массиве, даже если они не совпадают. Кажется, ему нужно найти только один подходящий объект, а затем он возвращает все. В приведенном ниже примере JSON есть две записи: B и H, и у каждого есть объекты внутри них: BBB, XXX, HHH и XXX. Если я ввожу BBB, мне возвращают BBB и XXX, но я не ожидаю увидеть XXX.

Моя JSON структура данных выглядит следующим образом:

[
  {
    "title": "b",
    "link": "b",
    "items": [
      {
        "title": "BBB",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
        "active": false
      },
      {
        "title": "XXX",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
        "active": false
      }
    ]
  },
  {
    "title": "h",
    "link": "h",
    "items": [
      {
        "title": "HHH",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
        "active": false
      },
      {
        "title": "XXX",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
        "active": false
      }
    ]
  }
]

Код вычисленного фильтра:

filteredPosts() {
  let compItems = this.compItems;
  if (this.filterTitle) {
    compItems = compItems.filter(item => {
      return item.items.some(subItem => {
        return (
          subItem.title
            .toLowerCase()
            .indexOf(this.filterTitle.toLowerCase()) !== -1
        );
      });
    });
  }

  return compItems;
}

Упрощенная версия моей разметки:

<input
    placeholder="Filter by Name"
    v-model="filterTitle"
/>

<div v-for="(item, index) in filteredPosts" :key="index">
    {{ item.title }}

    <ul v-for="(subItem, index) in item.items" :key="index">
        <li>{{ subItem.title }}</li>
    </ul>

    <hr />
</div>

Ответы [ 2 ]

1 голос
/ 06 апреля 2020

Ваш фильтр правильно находит все элементы с соответствующим заголовком в своем массиве подэлементов, но он не отфильтровывает несоответствующие подэлементы. Для этого просто добавьте еще одну строку для фильтрации подпунктов:

filteredPosts() {
  let compItems = this.compItems;
  if (this.filterTitle) {
    // find all items with a matching title in its `items[]`
    compItems = compItems.filter(item => ...);

    // filter out non-matching `items[]`
    compItems = compItems.map(item => {
      const items = item.items.filter(x => x.title.toLowerCase().indexOf(this.filterTitle.toLowerCase()) !== -1)
      return {
        ...item,
        items // overwrite `items[]` with our filtered one
      }
    })
  }

  return compItems;
}

new Vue({
  el: '#app',
  data() {
    return {
      filterTitle: '',
      compItems: []
    }
  },
  mounted() {
    this.compItems = [
      {
        "title": "b",
        "link": "b",
        "items": [
          {
            "title": "BBB",
            "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
            "active": false
          },
          {
            "title": "XXX",
            "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
            "active": false
          },
          {
            "title": "BBB 2",
            "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
            "active": false
          },
        ]
      },
      {
        "title": "h",
        "link": "h",
        "items": [
          {
            "title": "HHH",
            "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
            "active": false
          },
          {
            "title": "XXX",
            "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
            "active": false
          },
          {
            "title": "BBB 3",
            "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur libero ipsum, euismod posuere posuere ac, rhoncus eget nisi. Praesent ac lorem ut est fringilla porta.</p>",
            "active": false
          },
        ]
      }
    ]
  },
  computed: {
    filteredPosts() {
      let compItems = this.compItems;
      if (this.filterTitle) {
        compItems = compItems.filter(item => {
          return item.items.some(subItem => {
            return (
              subItem.title
              .toLowerCase()
              .indexOf(this.filterTitle.toLowerCase()) !== -1
            );
          });
        });
        
        compItems = compItems.map(item => {
          const items = item.items.filter(x => x.title.toLowerCase().indexOf(this.filterTitle.toLowerCase()) !== -1)
          return {
            ...item,
            items
          }
        })
      }

      return compItems;
    }
  }
})
<script src="https://unpkg.com/vue@2.6.11/dist/vue.min.js"></script>

<div id="app">
  <input
         placeholder="Filter by Name"
         v-model="filterTitle"
         />

  <div v-for="(item, index) in filteredPosts" :key="index">
    {{ item.title }}

    <ul v-for="(subItem, index) in item.items" :key="index">
      <li>{{ subItem.title }}</li>
    </ul>

    <hr />
  </div>
</div>
1 голос
/ 06 апреля 2020

Метод массива some просто вернет логическое значение (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)

Вы действительно хотите отобразить и отфильтровать, где заголовки подэлементов включают ваш filterTitle.

filteredPosts() {
    if (this.filterTitle) {
      return compItems.map(item => {
        return item.items.filter(subItem => {
          return subItem.title
            .toLowerCase()
            .includes(this.filterTitle.toLowerCase())
        });
      });
    }

    return this.compItems;
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...