3

I need to add a simple search and sort functionality to a VuetifyJS list. Here is a CodePen example of the list: https://codepen.io/anon/pen/bxGGgv

What is the standard way to do this in VueJS 2?

HTML:

<v-list two-line>
  <template v-for="(item, index) in items">
      <v-list-tile
        :key="item.title"
        avatar
        ripple
        @click="toggle(index)"
      >
        <v-list-tile-content>
           <v-list-tile-title>{{ item.title }}</v-list-tile-title>
           <v-list-tile-sub-title class="text--primary">
               {{ item.headline }}
           </v-list-tile-sub-title>
           <v-list-tile-sub-title>{{ item.subtitle }}</v-list-tile-sub-title>
        </v-list-tile-content>
      </v-list-tile>
      <v-divider
        v-if="index + 1 < items.length"
        :key="index"
      ></v-divider>
  </template>
</v-list>

JS:

  export default {
    data () {
      return {
        selected: [2],
        items: [
          {
            action: '15 min',
            headline: 'Brunch this weekend?',
            title: 'Ali Connors',
            subtitle: "I'll be in your neighborhood doing errands this weekend. Do you want to hang out?"
          },
          {
            action: '18hr',
            headline: 'Recipe to try',
            title: 'Britta Holt',
            subtitle: 'We should eat this: Grate, Squash, Corn, and tomatillo Tacos.'
          }
        ]
      }
    },
  }
gil
  • 1,318
  • 12
  • 19
Tom
  • 5,588
  • 20
  • 77
  • 129

2 Answers2

6

You can define a computed property in your class and do your filters. You can use this computed property as your filtering and sorting function.

Here is codepen

new Vue({
    el: '#app',
    data: {
        selected: [2],
        search: '',
        items: [{
                action: '15 min',
                headline: 'Brunch this weekend?',
                title: 'Ali Connors',
                subtitle: "I'll be in your neighborhood doing errands this weekend. Do you want to hang out?"
            },
            {
                action: '2 hr',
                headline: 'Summer BBQ',
                title: 'me, Scrott, Jennifer',
                subtitle: "Wish I could come, but I'm out of town this weekend."
            },
            {
                action: '6 hr',
                headline: 'Oui oui',
                title: 'Sandra Adams',
                subtitle: 'Do you have Paris recommendations? Have you ever been?'
            },
            {
                action: '12 hr',
                headline: 'Birthday gift',
                title: 'Trevor Hansen',
                subtitle: 'Have any ideas about what we should get Heidi for her birthday?'
            },
            {
                action: '18hr',
                headline: 'Recipe to try',
                title: 'Britta Holt',
                subtitle: 'We should eat this: Grate, Squash, Corn, and tomatillo Tacos.'
            }
        ]
    },
    computed: {
        filteredItems() {
            return _.orderBy(this.items.filter(item => {
                return item.title.toLowerCase().includes(this.search.toLowerCase()) ||
                    item.action.toLowerCase().includes(this.search.toLowerCase()) ||
                    item.headline.toLowerCase().includes(this.search.toLowerCase()) ||
                    item.subtitle.toLowerCase().includes(this.search.toLowerCase());
            }), 'headline');
        }
    },
    methods: {
        toggle(index) {
            const i = this.selected.indexOf(index)

            if (i > -1) {
                this.selected.splice(i, 1)
            } else {
                this.selected.push(index)
            }
        }
    }
})
Nima Boobard
  • 503
  • 2
  • 8
  • Thanks but the `| orderBy 'title'` seems not work anymore in VueJS 2. What would you recommend for VueJS 2? – Tom Aug 20 '18 at 18:17
  • 1
    Yes. You right. I almost forget. You should order your list within your computed property by using loadash (_). And also you should remove `| orderBy 'title'` from your template. Something like this: `return _.orderBy(item.title.includes(this.search), 'title')` – Nima Boobard Aug 21 '18 at 04:10
  • Thank you, works great but how to reset the search from a button? I updated your example Pen with a **clearable** option for the textfield: https://codepen.io/anon/pen/wEaBzX How to show all entries after clicking it? – Tom Aug 22 '18 at 14:03
  • 1
    You should handle clear button event manualy. I've editted my [example](https://codepen.io/prc0mputing/pen/qMBLNr?editors=0011) – Nima Boobard Aug 22 '18 at 19:39
6

This might not be the standard way but you could also try like this...

Filter the input in the search first by adding a v-model search and an array searchItem. You also need to initialized searchItem in the mounted hook. Then create a computed property filteredItems. I've used .filter() with .match() for flexibility if you will use regex and it returns an array.

But you could also use .includes() it depends on your choice

HTML (changes)

<v-toolbar>
   <v-text-field
     v-model="search" //add this
     ...
   ></v-text-field>
</v-toolbar>

<v-list two-line>
  <template v-for="(item, index) in filteredItems"> //change items to filteredItems
   ...
  </template>
</v-list>

JS:

data () {
  return {
    search: '',
    selected: [2],
    searchItem: [],
    items: [
       // your items here
    ]
  }
},

mounted() {
  setTimeout(() => this.searchItem = this.items)
},

computed: {
 filteredItems() {
    return this.searchItem.filter((item) =>{
         return item.title.toLowerCase().match(this.search)  || 
                item.headline.toLowerCase().match(this.search) || 
                item.subtitle.toLowerCase().match(this.search) || 
                item.action.toLowerCase().match(this.search)
    })
  }
}

Demo:

Your updated Codepen here

gil
  • 1,318
  • 12
  • 19
  • Thank you but I get this error: **Arrow function should not return assignment** for this: `setTimeout(() => this.searchItem = this.items)` What needs to be changed? – Tom Aug 21 '18 at 06:42
  • you could just use normal setTimeout() function or let it be `this.searchItem = this.items` – gil Aug 21 '18 at 06:48
  • Did you check the codepen? It's working fine there even when I tried in my PC. – gil Aug 21 '18 at 06:53
  • I guess it because of "babel-eslint": "^8.0.1" or "eslint": "^4.9.0" in my setup? – Tom Aug 21 '18 at 07:04