Home > Net >  Using Quasar q-select with a filter enabled when options is a json object
Using Quasar q-select with a filter enabled when options is a json object

Time:09-03

I cannot find any examples using composition api for this and could use some direction. I have a q-select which passes options as a prop using a axios request. The data is in this form:

[{description: "Apple Inc.", displaySymbol: "AAPL"}, {description: "Microsoft", displaySymbol: "MSFT"}]

I have about 20000 records in this JSON response. I am able to display it all in a v-select using:

          <q-select
          
          filled
          v-model="addStockSymbol"
          use-input
          input-debounce="0"
          label="Add New Stock Symbol"
          :options="stockTickers"
          option-label="description"
          option-value="displaySymbol"
          @blur="addPosition"
          @filter="filterFn"
          behavior="menu"
        >
          <template v-slot:no-option>
            <q-item>
              <q-item-section >
                No results
              </q-item-section>
            </q-item>
          </template>
        </q-select>

My issue is I do not know how to setup the filter and update function so I can search this. So far I have the code below but the examples on quasar do not use any arrays with objects but rather simple arrays. So I am wondering how do I approach this?

<script>
import {watch, ref, defineComponent,onMounted} from 'vue'
import {usePortfolioStore} from '../stores/portfolio-store'
import {storeToRefs} from 'pinia'
import {finnhubAPI} from 'boot/axios'

export default defineComponent({
  name: 'UploadPositions',
  components: {
    
  },
    setup () {
      //v-models
      const addStockSymbol = ref('')
      const addShareCount = ref('')
      const stockTickers = ref([])

      const loadData = () => {
        finnhubAPI.get('/api/v1/stock/symbol?exchange=US&token=tedkfjdkfdfd')
        .then((response) => {
          stockTickers.value = response.data
        })
        .catch(() => {
          console.log('API request failed')
        })
      }

      const filterFn = (val, update) => {
        if (val === '') {
          update(() => {
            stockTickers.value = 
          })
          return
        }
      }
      
      update(() => {
        const needle = val.toLowerCase()
        this.options = stringOptions.filter(v => v.toLowerCase().indexOf(needle) > -1)
      })
      
      //add on mount API request
      onMounted(() => {
        loadData()   
      })

    return {
      addStockSymbol, addShareCount, portfolio, addPosition, deletePosition, 
      loadData, stockTickers, modifyTickerData, filterFn, update
    }
  }
})
</script>

CodePudding user response:

Basically you need to store a complete copy of the response data and keep that around, untouched, so that each time the filter function is called you can filter off of that, looking within its objects for the label prop.

When setting up refs:

  //v-models
  const addStockSymbol = ref('')
  const addShareCount = ref('')
  const stockTickers = ref([])
  const allResponseData= ref([]) // <-- add this one

Then your loadData function:

const loadData = () => {
            finnhubAPI.get('/api/v1/stock/symbol?exchange=US&token=cc8ffgiad3iciiq4brf0')
            .then((response) => {
              
             const responseData = response.data.map((item) => ({label: item.description, value: item.displaySymbol}));
             allResponseData.value = [...responseData];
             stockTickers.value = [...responseData];
            })
            .catch(() => {
              console.log('API request failed')
            })
          }

Then in your filter function:

const filterFn =  (val, update, abort) => {
        update(() => {
          const needle = val.toLowerCase()
          stockTickers.value = allResponseData.value.filter(option => {
           return option.label.toLowerCase().indexOf(needle) > -1
          })
        })
      }

See it in action:

const { ref } = Vue

const stringOptions = [
  {label: 'Google', value: "goog"}, {label:'Facebook',value:'fb'}, {label:'Twitter', value: "twit"},{label: 'Apple', value: 'App'}]

const app = Vue.createApp({
  setup () {
    const options = ref(stringOptions)

    return {
      model: ref(null),
      options,

      filterFn (val, update, abort) {
        update(() => {
          const needle = val.toLowerCase()
          options.value = stringOptions.filter(option => {
           return option.label.toLowerCase().indexOf(needle) > -1
          })
        })
      }
    }
  }
})

app.use(Quasar, { config: {} })
app.mount('#q-app')
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material Icons" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/quasar.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/quasar.umd.prod.js"></script>
<!--
  Forked from:
  https://quasar.dev/vue-components/select#example--basic-filtering
-->
<div id="q-app" style="min-height: 100vh;">
  <div >
    <div >
      <q-select
        filled
        v-model="model"
        use-input
        hide-selected
        fill-input
        input-debounce="0"
        :options="options"
        @filter="filterFn"
        hint="Basic filtering"
        style="width: 250px; padding-bottom: 32px"
      >
        <template v-slot:no-option>
          <q-item>
            <q-item-section >
              No results
            </q-item-section>
          </q-item>
        </template>
      </q-select>
    </div>
  </div>
</div>

  • Related