v-autocomplete and setting user input as its value - vue.js

I use vuetify in my project and need typeahead component. Sadly v-autocomplete implemented as combobox with filter, so it doesn't allow setting user input as v-model (or at least I can't find I way how to do so).
Could someone please explain me how to implement such functionality (maybe by another vuetify component)? I load items from server, but they are serve just as suggestions. Users need to have an ability to type and set any value they want to.
Here is a base example https://codepen.io/miklever/pen/oMZxzZ. The problem is that if I type any word that doesn't start with 'John' v-autocomplete clears it on blur. I've tried to set v-model manually and to add user input to array, but any of this methods has issues and doesn't work as expected.
<div id="app">
<v-app>
<v-content>
<v-container>
<p>Name: {{ select || 'unknown'}}</>
<v-autocomplete
:items="items"
:search-input.sync="search"
v-model="select"
cache-items
flat
hide-no-data
label="Name"
></v-autocomplete>
</v-container>
</v-content>
</v-app>
</div>
new Vue({
el: "#app",
data: () => ({
items: [],
search: null,
select: null,
commonNames: ["John", "John2", "John3"]
}),
watch: {
search(val) {
val && val !== this.select && this.querySelections(val);
}
},
methods: {
querySelections(v) {
setTimeout(() => {
this.items = this.commonNames.filter(e => {
return (e || "").toLowerCase().indexOf((v || "").toLowerCase()) > -1;
});
}, 500);
}
}
});

In Vuetify 1.1.7 Combobox has new feature which you can refer to.
Its on the Advance custom options.

had this same issue and solved it with the v-combobox it's kinda like the same as v-autocomplete so, you can just replace your tags with <v-combobox></v-combobox> or check the documentation here: https://vuetifyjs.com/en/components/combobox/#usage

Related

Vuetify clear autocomplete input programmatically

can you take a look on this pen:
https://codepen.io/slayerbleast/pen/mdJMqwz
Why when you click on reset, it actually sets the input = null but on the input it's still showing the old value. You can get the real value clicking the other btn.
I'd would like to reset the autocomplete input with another btn like this pen instead of the clearable btn.
I tried to add: :search-input.sync="input"
But it causes undesired side effects... (for example it triggers the form validation automatically although it has lazy-validation attribute.
What do you think? Seems a bug? Setting the model to null should clear the input too.
Found the bug: https://github.com/vuetifyjs/vuetify/issues/10688
As noted in comments, this behavior changes in v2.2.15. The release notes show that the change is deliberate,
VAutocomplete: check for inital search input prop (#10642) (e09c916), closes #9757 #9757 #9757
Specifically, the code that changes is the VAutocomplete method setSearch()
setSearch () {
// Wait for nextTick so selectedItem
// has had time to update
this.$nextTick(() => {
if (
!this.multiple ||
!this.internalSearch ||
!this.isMenuActive
) {
this.internalSearch = (
!this.selectedItems.length ||
this.multiple ||
this.hasSlot
)
? this.internalSearch || null // "? null" in v2.2.14
: this.getText(this.selectedItem)
}
})
},
If you're happy with 'patching' within your app, this can be reversed by using a reference on the autocomplete
<template>
<div>
<v-autocomplete ref="autocomplete" v-model="input" :items="items" clearable></v-autocomplete>
<v-btn #click="reset">reset</v-btn>
<v-btn #click="value">get value</v-btn>
<div>{{input}}</div>
</div>
</template>
<script>
export default {
name: "playground",
data: () => ({
input: null,
items: ["a", "b", "c", "d"]
}),
mounted() {
console.clear();
console.log(this.$refs);
},
methods: {
value() {
alert("value: " + this.input);
},
reset() {
this.$nextTick(() => {
this.input = null;
this.$refs.autocomplete.internalSearch = null;
})
}
}
};
</script>
Codesandbox
You can use the built in reset function.
this.$refs[REF].reset()

Make Vuetify v-text-field component required by default

Currently, you can set rules that will work once the user changes the value of the input. For example:
Template part
<v-text-field
v-model="title"
label="Title"
></v-text-field>
Logic
export default {
data () {
return {
title: '',
email: '',
rules: {
required: value => !!value || 'Required.'
},
}
}
}
When the user focuses and removes focus from that element, or when the user deletes all its content, the required rule is triggered.
But what happens if we want to start with the rule enabled as soon as the component is mounted or created? Is there a way to achieve this?
I searched around vuetify but I could not find info about this nor examples in my humble google searches. I will appreciate help. I'm new in vue world. Thanks.
You could do the following:
Create a v-form and place your textfields inside the form. Don't forget to give your v-form a v-model and a ref too.
On mounted you can access the v-form via this.$refs and call .validate() just as Jesper described in his answer. In the codesandbox below you can see that the textfields immediately go red and display the "Required." text.
<v-form v-model="formValid" ref="myForm">
<v-text-field label="Field 1" :rules="rules.required"></v-text-field>
<v-text-field label="Field 2" :rules="rules.required"></v-text-field>
</v-form>
<script>
export default {
data() {
return {
formValid: false,
rules: {
required: [value => !!value || "Required."]
}
};
},
mounted() {
this.$refs.myForm.validate();
}
};
</script>
Example:
You should change your validation a little bit to achieve this.
<ValidationProvider rules="required" v-slot="{ errors }" ref="title">
<v-text-field
v-model="title"
label="Title"
></v-text-field>
</ValidationProvider>
And then you should call this.$refs.title.validate()
If you trigger this when mounted() is called, it should validate all the fields right away, as you're requesting.

How to use another method's variable in a vue component?

I have two methods in a vue component.
First makes the user choose from a v-select, either itemone or itemtwo. Then, to retreive the value for later i call #change to assign the variable to a method declared later - getItemValue.
Second is a submit button, when clicked, we go to handleSubmit.
After handleSubmit is called, I want to use the value I got from getItemValue (in variable theItem), but how can I call another method if it's out of my scope?
Mycomponent.vue
<template>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-select
v-model="select"
:items="items"
#change="getItemValue"
></v-select>
<v-btn
#click="handleSubmit"
>
Submit
</v-btn>
</v-form>
</template>
<script>
export default {
data: () => ({
items: [
'itemone',
'itemtwo'
],
}),
methods: {
getItemValue(theItem) {
},
handleSubmit(e) {
e.preventDefault()
// i need "theItem" here!
}
},
}
</script>
v-model already writes to your local variable, so there is absolutely no need to setup a get method to write the select value to a variable.
Actually, v-model is a bit more complicated than just 'write' to a variable, but the important bit is that in your template you are setting up v-model="select", which basically means that whenever the user uses the select to pick a value, your local select variable will be updated with the selected value.
Now, there is no select in your example component data, I don't know why. But if you had it, you could just sent that variable in your handleSubmit:
<template>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-select
v-model="select"
:items="items"
></v-select>
<v-btn
#click="handleSubmit"
>
Submit
</v-btn>
</v-form>
</template>
<script>
export default {
data: () => ({
select: '',
items: [
'itemone',
'itemtwo'
],
}),
methods: {
handleSubmit(e) {
e.preventDefault()
doSomethingWith(this.select); // this will be updated at this point
// with the option the user selected
}
},
}
</script>
Now, however, be aware that if the select variable is a component prop, then you should not do this right away, since props are not intended to be modified directly by child components. If that would be the case, please update your question with more info.
You would simple set the variable (theItem) value to the data
getItemValue(theItem) {
this.theItem;
},
and then retrieve it later
handleSubmit(e) {
e.preventDefault()
// i need "theItem" here!
// simple access theItem
console.log('theItem', this.theItem);
}

input box automatically changed to original value

When I change the input box, it will automatically change to original value by itself. It seems the reason is btnDisabled has been changed, because if I remove the line of this.btnDisabled = !this.btnDisabled; the input box will no more be automatically changed. I want to know why btnDisabled will affect the input box's value?
const vm = new Vue({
el: '#app',
data: {
btnDisabled: false,
total: 25,
},
created() {
setInterval(() => {
this.btnDisabled = !this.btnDisabled;
}, 500);
},
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<input type="text" v-bind:value="total">
<button v-bind:disabled="btnDisabled">test</button>
</div>
This is because Vue is rerendering the "component" and the value is still technically 25. You can use v-model or #input to update the data or you can use v-once to prevent Vue from rerendering the input text box.
const vm = new Vue({
el: '#app',
data: {
btnDisabled: false,
total: 25,
},
created() {
setInterval(() => {
this.btnDisabled = !this.btnDisabled;
}, 500);
},
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<input type="text" v-bind:value="total" v-once>
<button v-bind:disabled="btnDisabled">test</button>
</div>
There is an answer by Steven B which might solve your problem but I would like to add a little bit about the problem identifying the cause. The problem is in the one-way-binding, what I mean is that, when you are using the following:
<input type="text" v-bind:value="total">
You are introducing a new separate state in DOM by allowing the user typing in the input. So, when the user types into the input, the data.total property is not being updating, it's still 25 but the DOM input has new value. In that case, when setInterval fires and data.btnDisabled is updated, the Application's state is changed and then VUE just force the component to be re-render to keep the data and the DOM in sync. I would prefer v-model instead of :value.

vuetify v-select options from json array of objects

I am trying to populate a vuetify v-select from a rest api json response. Below is the code I have so far. Populating the list using a simple array is no problem but I am having difficutly setting both the value and text properties.
<template>
<v-container fluid>
<v-slide-y-transition mode="out-in">
<v-layout column align-center>
<v-select v-model="dbSelect" v-on:change="dbConnect()" :items="dbOptions" single-line></v-select>
</v-layout>
</v-slide-y-transition>
</v-container>
</template>
<script>
import axios from 'axios'
export default {
name: 'HelloWorld',
data () {
return {
dbSelect: '',
dbOptions: [],
}
},
mounted () {
axios.get('http://localhost:5000/api/values')
.then(r => {
// var formatted = []
for (let i = 0; i < r.data.length; i++) {
this.dbOptions.push(r.data[i])
// formatted[i] = { value: r.data[i].id, text: r.data[i].name }
}
},
error => {
console.error(error)
}
)
}
}
</script>
This is the simple Json dataset:
[{"id":1,"name":"A"},{"id":2,"name":"B"},{"id":3,"name":"C"},{"id":4,"name":"D"},{"id":5,"name":"E"}]
Currently I am pushing in the whole object because I have been trying to use item-value and item-text but according to documentation I can see that is not right. r.data[i].id and r.data[i].name produce a drop-down list as expected. I want to set dbSelect to the value of the selection on change for a secondary call after selection.
Many thanks all for your help.
I had the same issue and felt this needed to be added as the correct answer that solved the issue.
Using the properties of item-text="" and item-value=""
<v-select
:items="dbOptions"
v-model="dbSelect"
item-text="name"
item-value="id"
></v-select>
The array of objects using name and id
[{"id":1,"name":"A"},{"id":2,"name":"B"},{"id":3,"name":"C"},{"id":4,"name":"D"},{"id":5,"name":"E"}]
See Example
This was originally answered by #Bert in the comments.