Qiuzman
Qiuzman

Reputation: 1751

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

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
          class="grey-7"
          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 class="text-grey">
                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>

Upvotes: 2

Views: 8067

Answers (1)

WillD
WillD

Reputation: 6552

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 class="q-pa-md">
    <div class="q-gutter-md row">
      <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 class="text-grey">
              No results
            </q-item-section>
          </q-item>
        </template>
      </q-select>
    </div>
  </div>
</div>

Upvotes: 4

Related Questions