简体   繁体   中英

How to filter results using "v-for" in Vue.JS

I am using a tabbed interface built with Bootstrap-Vue. When clicking on one of these tabs I'm using v-for to render some data in an accordion/dropdown which is displayed as a grid.

Currently, I have a tab that shows all results which works fine when using the following (Note: prizesData is just where I'm storing response.data after I have called in with axios):

<div class="col-sm-12 col-md-6 col-lg-3 w-100 prize-dropdown-wrapper pb-5"
 v-for="(prize, name, index) in prizesData"
 :key="index"
>
<!--REMAINDER OF MARKUP GOES HERE-->
</div>

My JSON structure looks something like this, except I have 43 results in total. Every value in each object will change, but I want to use prizeType1Name and prizeType2Name to check if they contain the strings " CTW ", " IW " or " OG ":

{
  "1": [
      {
         "prizeTitle": "",
         "prizeImage": "",
         "prizeFirstLineText": "",
         "prizeType1": {
            "Image": "",
            "Line1": "",
            "Line2": ""
         },
         "prizeType2": {
            "Image": "",
            "Line1": "",
            "Line2": ""
         },
         "prizeTermsText": "",
         "prizeType1Name": "CTW",
         "prizeType2Name": ""
      }
   ],
  "2": [
      {
         "prizeTitle": "",
         "prizeImage": "",
         "prizeFirstLineText": "",
         "prizeType1": {
            "Image": "",
            "Line1": "",
            "Line2": ""
         },
         "prizeType2": {
            "Image": "",
            "Line1": "",
            "Line2": ""
         },
         "prizeTermsText": "",
         "prizeType1Name": "IW",
         "prizeType2Name": ""
      }
   ]
}

To access either of these values I am using:

this.prizesData[name][0].prizeType1 or this.prizesData[name][0].prizeType2

I'm having difficulty understanding how to filter my results. For example, if I click on the tab named ( CTW ), I want to show only the results that contain "CTW" in the JSON file. Or if the tab named "IW" is clicked then the results should be for the objects that contain "IW".

What I have tried:

Added filteredPrizes to store filtered prizes as an object to access later

data() {
    return {
      prizesData: {},
      filteredPrizes: {},
    };
  }

I tried to use filter() on the existing prizesData object I received from the JSON file, I added this in the same method where I'm fetching the JSON file.

getPrizesInfo() {
      const self = this;
      //Get Error Validation JSON
      axios
        .get("/data/prizes.json")
        .then(response => (this.prizesData = response.data));

      this.filteredPrizes = this.prizesData.filter(prize =>
        this.prizesData[name][0].prizeType1.includes("CTW")
      );
    }

What I'm expecting: I have 4 tabs, 1st one works fine (Shows all). The other 3 need to show only filtered results depending on if " CTW " " IW " or " OG " exist in either prizeType1Name or prizeType2Name .

I am not sure I understood correctly, but you can try:

 new Vue({ el: '#app', data() { return { myData: { prizeTitle: '', prizeImage: '', prizeFirstLineText: '', prizeType1: { Image: 'CTW', Line1: 'CTW', Line2: 'CTW' }, prizeType2: { Image: 'IW', Line1: 'IW', Line2: 'IW' }, prizeTermsText: '', prizeType1Name: 'CTW', prizeType2Name: 'IW' }, prizesData: {} } }, methods: { filter (criteria) { // getting keyName with value equals to criteria const key = Object.keys(this.myData) .filter(key => this.myData[key] === criteria) .pop().replace('Name', '') this.prizesData = this.myData[key] } } })
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script> <div id="app"> <button @click="filter('CTW')">CTW</button> <button @click="filter('IW')">IW</button> <pre>{{ prizesData }}</pre> </div>

  • Have your filteredPrizes be a comupted property
  • Create an Array of Objects for your criteria (may have refactor the idea to suit your needs or be more dynamic)
// Inside data
filterCriteria: [
  { name: 'prizeType1Name', value: 'CTW' },
  { name: 'prizeType2Name', value: 'CTW' }
];
  • Create a function to return an Array of Objects
    • create an empty Array to push our selections into
    • loop through the prizes Object by keys
    • inside the loop, iterate over the critera
    • assign the selection to the Array, provided it doesn't already exist
    • return the Array
// Inside methods
getFilteredPrizes(prizes, criteria) {
  selected = [];

  Object.keys(prizes).forEach(a => {
    criteria.forEach(b => {
      if (prizes[a][0][b.name] === b.value && !selected.contains(prizes[a][0])) {
        selected.push(prizes[a][0]);
      }
    });
   });

   return selected;
}

 new Vue({ el: '#app', data() { return { prizesData: { "1": [ { "prizeTitle": "", "prizeImage": "", "prizeFirstLineText": "", "prizeType1": { "Image": "", "Line1": "", "Line2": "" }, "prizeType2": { "Image": "", "Line1": "", "Line2": "" }, "prizeTermsText": "", "prizeType1Name": "CTW", "prizeType2Name": "" } ], "2": [ { "prizeTitle": "", "prizeImage": "", "prizeFirstLineText": "", "prizeType1": { "Image": "", "Line1": "", "Line2": "" }, "prizeType2": { "Image": "", "Line1": "", "Line2": "" }, "prizeTermsText": "", "prizeType1Name": "IW", "prizeType2Name": "" } ] }, filterCriteria: [ { name: 'prizeType1Name', value: 'CTW' }, { name: 'prizeType2Name', value: 'CTW' } ] } }, computed: { filteredPrizes() { return this.getFilteredPrizes(this.prizesData, this.filterCriteria); } }, methods: { getFilteredPrizes(prizes, criteria) { selected = []; Object.keys(prizes).forEach(a => { criteria.forEach(b => { if (prizes[a][0][b.name] === b.value && !selected.includes(prizes[a][0])) { selected.push(prizes[a][0]); } }); }); return selected; } } });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script> <div id="app"> <h3>Filtered Prizes</h3> <pre>{{ filteredPrizes }}</pre> </div>

As @seantunwin suggested, I changed my JSON from an object to an array - required some tidying up and formatting but made life easier in the long run.

So my new structure now looks like this:

[

{

"id": 1,

"prizeTitle": "",

"prizeImage": "",

"prizeFirstLineText": "",

"prizeType1": {

"Image": "",

"Line1": "",

"Line2": ""

},

"prizeType2": {

"Image": "",

"Line1": "",

"Line2": ""

},

"prizeTermsText": "",

"prizeType1Name": "CTW",

"prizeType2Name": ""

}

]

I then used a computed property advised by @Our_Benefactors like this:

computed: {

filteredPrizesByQuery: function() {

*return* this.prizes.filter(prize => {

*return* (

prize.prizeType1Name.match(this.query) ||

prize.prizeType2Name.match(this.query)

);

});

}

}

I wrapped my code that I want to loop through using v-for and referenced the computed property:

<div

class="col-sm-12 col-md-6 col-lg-3 w-100 prize-dropdown-wrapper pb-5"

v-for="(prize, index) in filteredPrizesByQuery"

:key="index"

>

I set query as an empty string as default in data

data() {

*return* {

query: "",

prizes: [],

}

};

I added a click event on the tabs to change the query value, similar to @christiancarrillo answer

<b-tab title="IW" @click="query='IW'">

So now when I click on a tab the value changes to CTW, IW or OG, and the computed property checks through the values and returns the results that match the query.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM