【问题标题】:Vue filtering retuning all Objects in an Array even if they don't match the search criteriaVue过滤重新调整数组中的所有对象,即使它们不匹配搜索条件
【发布时间】:2020-07-17 22:37:11
【问题描述】:

我正在 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>

【问题讨论】:

    标签: vue.js vuejs2 filtering


    【解决方案1】:

    数组方法 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;
      }
    

    【讨论】:

    • 啊,这解释了为什么我的不起作用。我已经更新到你的版本,它似乎正在过滤我需要的二级对象(太棒了,谢谢!!)唯一的是返回的数据结构是一个级别下降(已经在项目内)所以我失去了其中的上下文对象也属于词汇表字母,对于保持相同的 JSON 结构有什么建议吗?
    • 如果你想设置一个 jsbin,我很乐意为你提供帮助。
    • 噢,以前没用过。我在这里设置了一个,组件不会使用此示例中的新数据重新渲染,但您可以看到在应用过滤器时数据正在更新 - jsbin.com/guwitav/edit?html,js,output
    【解决方案2】:

    您的过滤器可以正确查找在其子项数组中具有匹配标题的所有项,但它不会过滤掉不匹配的子项。为此,只需添加另一行来过滤子项:

    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>

    【讨论】:

    • 太棒了!!我没有遇到这三个点,所以只是读了一遍,不知道你能做到这一点!非常感谢!
    • @Alex 没问题 :)
    猜你喜欢
    • 2018-07-15
    • 2021-01-20
    • 2018-06-21
    • 1970-01-01
    • 2018-06-22
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    相关资源
    最近更新 更多