q-select
-
Hi friends
in the q-select component in the options, I assign it the model array, but it doesn’t show it
my structure in the model is:
options: {
companies: [],
employess: []
} -
I think it only accepts a defined structure like this
{
label: ‘Uno’,
value: ‘1’,
description: ‘uno’,
category: ‘1’
}. -
@eloy-silva Have a look to
option-value
andoption-label
props ofq-select
<q-select ... :options="options.companies" option-value="USR_ERP_COMPANY_ID" option-label="USR_ERP_NAME" ... />
These props can also be a function if you need more control.
-
@tof06 Thanks
-
@tof06 I tried it and it works … but now the filter function gives error in toLowerCase
TypeError: Cannot read property ‘toLowerCase’ of undefined"
-
@eloy-silva Pretty hard to guess why without viewing the code !
-
@tof06 Hi,
Hello, thanks for answering
I am new to this framework (vuejs, quasar), and what I want to do is a reusable component, so as not to be repeating the filter functions, I just want to send the json object and that when filtering or selecting the item, it is reflected in my UI and in the model
excuse my spelling, I don’t speak english
<template> <q-select square dense outlined use-input input-debounce="0" v-model="valuemember" :option-value="optionvalue" :option-label="optionlabel" :options="dataset" :placeholder="placeholder" @filter="filterFn" > </q-select> </template> <script> export default { name: "optimusSelect", props: { dataSource: { type: Array, required: true, twoWay: true }, optionvalue: { type: String, required: true }, optionlabel: { type: String, required: true }, placeholder: { type: String, required: false } // displaymember: { // type: String, // required: true, // twoWay: true // }, // valuemember: { // type: String, // required: true, // twoWay: true // } }, computed: { dataset: { get() { return this.dataSource; }, set(value) { this.dataSource = value; } } // datavalue:{ // get(){ // return this.valuemember; // }, // set(value){ // this.valuemember = value; // } // } }, data() { return { valuemember: null }; }, mounted() {}, methods: { filterFn(val, update) { var that = this; if (val === "") { update(() => { //this.dataSource = objectOptions // with Quasar v1.7.4+ // here you have access to "ref" which // is the Vue reference of the QSelect }); return; } update(() => { const needle = val.toLowerCase(); let data = that.dataSource; that.dataset = data.filter( v => v.value.toLowerCase().indexOf(needle) > -1 ); }); } } }; </script> <style></style>
in my page
<template> <q-page padding class="page-normal"> <q-form> <q-toolbar> <q-toolbar-title>Nuevo usuario</q-toolbar-title> </q-toolbar> <separatorH caption="Datos generales"></separatorH> <div class="row q-pa-md"> <div class="f-v-label col-2">Compañia:</div> <div class="f-v-input col-2"> <optimusSelect v-model="oEntity.USR_ERP_COMPANY_ID" :dataSource="options.companies" optionvalue="USR_ERP_COMPANY_ID" optionlabel="USR_ERP_NAME" /> </div> </div> <div class="row q-pa-md"> <div class="f-v-label col-2">Empleado:</div> <div class="f-v-input col-2"> <!-- <lookup :dataSource="cities" :selection.sync="datavalue" placeholder="Buscar empleado"></lookup> --> </div> </div> <div class="row q-pa-md"> <!--Label--> <div class="f-v-label col-2">Nombre de usuario:</div> <!--Input--> <div class="f-v-input col-2"> <q-input square outlined autofocus dense v-model="datav" placeholder="Nombre de usuario:" > </q-input> </div> </div> </q-form> </q-page> </template> <script> import { mapState, mapActions } from "vuex"; import separatorH from "components/separatorH"; import lookup from "components/lookup"; import optimusSelect from "components/optimus-select"; export default { name: "user", data() { return { options: { companies: [ { USR_ERP_COMPANY_ID: 100, USR_ERP_NAME: 'ACNE', }, { USR_ERP_COMPANY_ID: 200, USR_ERP_NAME: 'SOCONSA', }, ] }, datav:null, oEntity: { USR_ERP_COMPANY_ID: '', USR_ERP_NAME: "" } }; }, components: { separatorH, lookup, optimusSelect }, computed: { ...mapState("store", ["userDetails", "currentPage"]) }, methods: { ...mapActions("store", ["load_page"]) }, mounted() { // this.load_page({ // title: "Usuario", // IsActive: true // }); }, destroyed() { // this.load_page({ // title: "", // IsActive: false // }); } }; </script>
-
toLocaleLowerCase solutions !!!
but in the filter function I have an error:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value. Prop being mutated: “dataSource”
try to fix it with computed:
<template> <q-select square dense outlined v-model="valuemember" :option-value="optionvalue" :option-label="optionlabel" :options="dataSource" :placeholder="placeholder" use-input hide-selected fill-input input-debounce="0" @filter="filterFn" @input-value="setModel" @input="onChange" emit-value map-options > <template v-slot:no-option> <q-item> <q-item-section class="text-grey"> No results </q-item-section> </q-item> </template> </q-select> </template> <script> export default { watch: {}, computed: { dataset:{ get(){ return this.dataSource; }, set(value){ this.dataSource = value; this.$emit("dataset", value); } } }, data() { return { valuemember: null, }; }, mounted(){ }, props: { dataSource: { type: Array, required: true, twoWay: true, }, optionvalue: { type: String, required: true, }, optionlabel: { type: String, required: true, }, placeholder: { type: String, required: false, }, }, methods: { filterFn(val, update, abort) { if (val === '') { update(() => { //this.dataSource = this.dataset // with Quasar v1.7.4+ // here you have access to "ref" which // is the Vue reference of the QSelect }) return } update(() => { const needle = val.toLocaleLowerCase(); this.dataSource = this.dataset.filter( (v) => v[this.optionlabel].toLocaleLowerCase().indexOf(needle) > -1 ); this.$emit("dataset", this.dataset); }); }, setModel(val) { this.model = val; }, onChange(value) { //refresh and update model this.$emit("input", value); }, getData() { return this.valuemember; }, setData(value) { this.valuemember = value; this.$emit("input", value); }, }, }; </script>
-
@eloy-silva As stated in VueJS Documentation, props are only one-way binding from parent to child, so, you can’t update them from child.
Depending on the behavior you want, a simple
data
property will do the trick :export default { props: { dataSource: { type: Array, required: true }, // ... }, data() { return { // ... _dataSource: this.dataSource } }
Then, use
_dataSource
in your template instead ofdataSource
If you absolutly need
dataSource
to be sync in parent component, then, you’ll need to implement an event, and listen to this event on parent -
-
@tof06
the problem with the props, it was overcome
but according to what you indicate and following the example in the documentation, the filter function should look like this:
and not workfilterFn(val, update, abort) { update(() => { const needle = val.toLocaleLowerCase(); this._dataSource = this.dataSource.filter( (v) => v[this.optionlabel].toLocaleLowerCase().indexOf(needle) > -1 ); }); },
now in the documentation everything works fine, there it uses a constant of a string array, but when changing to a json object, things get very complicated
-
@tof06
Follow your advice, check the documentation of props in VueJS and it is already solved
I post it here if it serves someone or see how it can be improved.in the component:
<!-- select autocomplete --> <template> <q-select v-model="valuemember" use-input hide-selected fill-input input-debounce="0" :option-value="optionvalue" :option-label="optionlabel" :options="dataSource" :placeholder="placeholder" @filter="filterFn" @input-value="setModel" square dense outlined @input="onChange" options-dense emit-value map-options > <template v-slot:no-option> <q-item> <q-item-section class="text-grey"> No hay resultados </q-item-section> </q-item> </template> </q-select> </template> <script> let filtered; export default { computed: { // _dataSource: { // get() { // return this.dataSource; // }, // set(value) { // //console.log('set'); // this.$props.dataSource = value // //this.$emit("dataSource", value); // }, // }, }, data() { return { valuemember: 100, _dataSource : this.dataSource }; }, watch: {}, created() {}, mounted() {}, props: { dataSource: { type: Array, required: true, }, optionvalue: { type: String, required: true, }, optionlabel: { type: String, required: true, }, placeholder: { type: String, required: false, }, }, methods: { filterFn(val, update, abort) { filtered = this._dataSource update(() => { const needle = val.toLocaleLowerCase(); // // this.dataSource = this.$data._dataSource.filter( // (v) => v[this.optionlabel].toLocaleLowerCase().indexOf(needle) > -1 // ); this._dataSource = this.$data._dataSource.filter( (v) => v[this.optionlabel].toLocaleLowerCase().indexOf(needle) > -1 ); }); this.$emit('update-datasource', this._dataSource) }, setModel(val) { this.valuemember = val; }, onChange(value) { //refresh and update model this.$emit("input", value); }, getData() { return this.valuemember; }, setData(value) { this.valuemember = value; this.$emit("input", value); }, }, }; </script>
in the page:
<OptimusSelect2 ref="sel2" v-model="USR_ERP_COMPANY_ID" :dataSource="options.companies" optionvalue="USR_ERP_COMPANY_ID" optionlabel="USR_ERP_NAME" @update-datasource="UpdateCompany" />
data() { return { options: { companies: [ { USR_ERP_COMPANY_ID: 100, USR_ERP_NAME: 'ACME', }, { USR_ERP_COMPANY_ID: 200, USR_ERP_NAME: 'SOCONSA', }, ] },
UpdateCompany(data){ //Update this.options.companies = data; }
your comments helped me research and learn more
thank you so muchEloy