v-mask not working correctly with veutifyjs input - vue.js

I'm using veutifyjs text-input to show phone number. I have a button to assign phone number to v-model. But after assign it not change there text input. if I enter some think on text-input v-mask work. But if I assign value to v-model it not working. Also i'm tying next trick but its not have any difference.
<v-text-field
v-model="form.phone_number"
v-mask="'(###) ###-####'"
dense
outlined
autocomplete="new-password"
hide-details="auto"
label="Phone Number"
placeholder="Phone Number"
></v-text-field>
<v-btn color="warning" #click="dataupdate()">Add</v-btn>
dataupdate() {
this.$nextTick(() => {
this.form.phone_number = '4032223344'
})
},

You have to set the v-mask dynamically after you set the value of form.phone_number, so we could create a phoneNumberMask variable:
data() {
return {
phoneNumberMask: '',
};
}
Set it as the v-mask value:
<v-text-field
v-model="form.phone_number"
v-mask="phoneNumberMask"
...
then at dataupdate():
dataupdate() {
this.form.phone_number = '4032223344'
this.$nextTick(() => {
this.phoneNumberMask = '(###) ###-####'
})
/*
* you'd just have to deal with calling this funcition
* when phoneNumberMask has already a value
* and that depends on your business rules
*/
},

I had the same issue after migration to Vuetify 2 and fixed it so: Just replaced v-mask library with vue-input-facade as mentioned here.
It works exactly the same as the old mask from the first Vuetify but you should use the parameter "v-facade" instead.

Related

How can I implement v-model.number on my own in VueJS?

I have a text field component for numeric inputs. Basically I'm just wrapping v-text-field but in preparation for implementing it myself. It looks like this.
<template>
<v-text-field v-model.number = "content" />
</template>
<script>
export default {
name: 'NumericTextField',
props: [ 'value' ],
computed: {
content: {
get () { return this.value },
set (v) { this.$emit('input', f) },
},
}
}
</script>
This has generated user feedback that it's annoying when the text field has the string "10.2" in it and then backspace over the '2', then decimal place is automatically delete. I would like to change this behavior so that "10." remains in the text field. I'd also like to understand this from first principles since I'm relatively new to Vue.
So I tried this as a first past, and it's the most instructive of the things I've tried.
<template>
<v-text-field v-model="content" />
</template>
<script>
export default {
name: 'NumericTextField',
props: [ 'value' ],
computed: {
content: {
get () { return this.value },
set (v) {
console.log(v)
try {
const f = parseFloat(v)
console.log(f)
this.$emit('input', f)
} catch (err) {
console.log(err)
}
},
},
}
}
</script>
I read that v-model.number is based on parseFloat so I figured something like this must be happening. So it does fix the issue where the decimal place is automatically deleted. But... it doesn't even auto delete extra letters. So if I were to type "10.2A" the 'A' remains even though I see a console log with "10.2" printed out. Furthermore, there's an even worse misfeature. When I move to the start of the string and change it to "B10.2" it's immediately replaced with "NaN".
So I'd love to know a bunch of things. Why is the body of the text body immediately reactive when I change to a NaN but not immediately reactive when I type "10.2A"? Relatedly, how did I inadvertently get rid of the auto delete decimal place? I haven't even gotten to that part yet. So I'm misunderstanding data flow in Vue.
Lastly, how can I most simply provide a text box that's going to evaluate to a number for putting into my data model but not have the annoying auto delete of decimal places? The existing functionality doesn't auto delete trailing letters so I'm guessing the auto delete of decimal places was a deliberate feature that my users don't like.
I'm not 100% sure of any of this, but consider how v-model works on components. It basically is doing this:
<v-text-field
v-bind:value="content"
v-on:input="content = $event.target.value"
/>
And consider how the .number modifier works. It runs the input through parseFloat, but if parseFloat doesn't work, it leaves it as is.
So with that understanding, I would expect the following:
When you type in "10.2" and then hit backspace, "10." would be emitted via the input event, parseFloat("10.") would transform it to 10, v-on:input="content = $event.target.value" would assign it to content, and v-bind:value="content" would cause the input to display "10". So then, this is the expected behavior.
When you type in "10.2" and then hit "A", "10.2A" would be emitted via the input event, parseFloat("10.2A") would transform it to 10.2, v-on:input="content = $event.target.value" would assign it to content, and v-bind:value="content" would cause the input to display "10.2". It looks like it's failing at that very last step of causing the input to display "10.2", because the state of content is correctly being set to 10.2. If you use <input type="text" v-model.number="content" /> instead of <v-text-field v-model.number="content" />, once you blur, the text field successfully gets updated to "10.2". So it seems that the reason why <v-text-field> doesn't is due to how Vuetify is handling the v-bind:value="content" part.
When you type in "10.2" and then enter "B", in the beginning, "B10.2" would be emitted via the input event, parseFloat("B10.2") would return NaN, and thus the .number modifier would leave it as is, v-on:input="content = $event.target.value" would assign "B10.2" to content, and v-bind:value="content" would cause the input to display "B10.2". I agree that it doesn't seem right for parseFloat("10.2A") to return 10.2 but parseFloat("B10.2") to return "B10.2".
Lastly, how can I most simply provide a text box that's going to evaluate to a number for putting into my data model but not have the annoying auto delete of decimal places?
Given that the default behavior is weird, I think you're going to have to write your own custom logic for transforming the user's input. Eg. so that "10.2A" and "B10.2" both get transformed to 10.2 (or are left as is), and so that decimals are handled like you want. Something like this (CodePen):
<template>
<div id="app">
<input
v-bind:value="content"
v-on:input="handleInputEvent($event)"
/>
<p>{{ content }}</p>
</div>
</template>
<script>
export default {
data() {
return {
content: 0,
};
},
methods: {
handleInputEvent(e) {
this.content = this.transform(e.target.value);
setTimeout(() => this.$forceUpdate(), 500);
},
transform(val) {
val = this.trimLeadingChars(val);
val = this.trimTrailingChars(val);
// continue your custom logic here
return val;
},
trimLeadingChars(val) {
if (!val) {
return "";
}
for (let i = 0; i < val.length; i++) {
if (!isNaN(val[i])) {
return val.slice(i);
}
}
return val;
},
trimTrailingChars(val) {
if (!val) {
return "";
}
for (let i = val.length - 1; i >= 0; i--) {
if (!isNaN(Number(val[i]))) {
return val.slice(0,i+1);
}
}
return val;
},
},
};
</script>
The $forceUpdate seems to be necessary if you want the input field to actually change. However, it only seems to work on <input>, not <v-text-field>. Which is consistent with what we saw in the second bullet point. You can customize your <input> to make it appear and behave like <v-text-field> though.
I put it inside of a setTimeout so the user sees "I tried to type this but it got deleted" rather than "I'm typing characters but they're not appearing" because the former does a better job of indicating "What you tried to type is invalid".
Alternatively, you may want to do the transform on the blur event rather than as they type.

How to prevent reactivity in form placeholder

I have an input field set up as
<input #input="e => machineCIDR = e.target.value" type="text" name="machine" class="form-control form-control-lg" :placeholder="machineCIDR">
The problem is, whenever someone fills out the form and then deletes it all, the placeholder is left with the last character that was filled out.
How can I setup :placeholder="machineCIDR" so that it shows the initial value, and then never gets updated again??
Assuming machineCIDR is set up already (as a prop or in data), you could create an object for storing all of your initial values:
data() {
return {
...
initial: {}
}
}
And set up the values in created:
created() {
this.initial['machineCIDR'] = this.machineCIDR;
}
Then bind to that instead in the placeholder:
:placeholder="initial['machineCIDR']"

Fill with values - dynamic dropdowns

I'm trying to fill dropdowns with values while dropdowns are "sent" trough props. I'm getting the data from server correctly, and storing it in array where I am using that array to fill selected dropdown.
This problem looks like its Vuetify or Vue bug but I'm not sure.
Code for dynamic dropdowns looks like this (Vuetify):
<v-col cols="12" lg="2" md="2" sm="6" xs="6" v-for="(dropdowns, index) in dropdownNumber" v-bind:key="index">
<v-select
:v-model="dropdowns.field_name"
:loading="loadingInputs"
:label="dropdowns.dropdown_name"
:disabled="enableDropdowns"
:items="getData[dropdowns.field_name]"
:clearable="true"
:item-text="dropdowns.field_name"
:item-value="dropdowns.field_name"
:item-key="dropdowns.field_name"
#mousedown="changeFields(dropdowns.field_name, index)"
#change="selectedValue(dropdowns.field_name, $event)"
:multiple="dropdowns.multiple"
>
</v-select>
And function for take data from server:
async getDropdownData($event, index) {
const key = $event
try {
this.values['sector_name'] = this.categoryName()
this.keys.forEach(element => {
if(localStorage.getItem(element) != null) {
this.values[element] = localStorage.getItem(element)
}
});
this.takeServerData = (await DataService.getDropdownData({
existingValues: this.values,
selectedDropdown: $event
})).data
this.getData[key] = this.takeServerData
console.log(this.getData)
} catch (error) {
this.error = "Dogodila se pogreška prilikom dohvaćanja vrijednosti za tu opciju."
}
},
Sometimes, while I'm editing my code it normally fill already clicked dropdown, but after refreshing site it does not work.
Photo example: https://imgur.com/a/MutkZQC
Is there something I'm missing?
Any help or advice is welcome!
It sounds like a reactivity problem related to nested objects.
https://v2.vuejs.org/v2/guide/reactivity.html
Either sure your arrays are predefined within the getData object.
data () {
return {
getData: {
field1 : [],
field2 : []
}
}
},
Or update your getData object like this
this.$set(this.getData, key, this.takeServerData)

Vuetify combobox - How to disable typing in vuetify's combobox

The vuetify combobox is allowing the user to type inside the combobox. Any clue on how to disable this.
This is how I have implemented my combobox.
<v-combobox
:loading="isSurveyBeingPopulated"
class="static--inputs"
color="red"
box
:items="folders"
:rules="[rules.required]"
item-text="value"
dense
placeholder="Select Survey Folder"
item-value="key"
slot="input"
v-model="selectedSurveyFolder">
</v-combobox>
Combobox:
The v-combobox component is a v-autocomplete that allows the user to
enter values that do not exist within the provided items. Created
items will be returned as strings.
So if you want to disable the typing you should use a select: https://vuetifyjs.com/en/components/selects
Select fields components are used for collecting user provided
information from a list of options.
You can just delete new item after changed.
<v-combobox
v-model="selected"
:items="[...selected, ...items]"
multiple
#change="onChangeCombobox"
/>
onChangeCombobox(items) {
this.selected = items.filter((item) => this.items.includes(item))
}
I had the same problem, I used v-select instead of v-combobox, it works fine:
<v-select
return-object
></v-select>
I've gone thru the same, i need combobox to list item and custom values but i must disable typing, my solutions was to use key events and "change"its behavior like:
#keydown="$event.target.blur()"
#keypress="$event.target.blur()"
#keyup="$event.target.blur()"
Yes, you can achieve filtering of a combobox by using the rules code, e.g.
<v-combobox
v-model="selection"
:items="items"
:search-input.sync="input"
:rules="intRule"
chips
clearable
dense
multiple
small-chips
item-text="title"
autocomplete="off"
return-object
>
</v-combobox>
and then in your script section in data, sth like
data() {
return {
selection: [],
input: null,
items: [],
valid: false,
intRule: [
function(v) {
if (!v || v.length < 0) {
return false
} else if (v.length > 0) {
for (let i = 0; i < v.length; i++) {
if (/[^0-9]/g.test(v[i].id)) {
v.splice(-1, 1)
return false
}
}
return false
} else {
return true
}
}
]
}
}
the input could be used to overwrite a no results slot and show for what input are no results found.
hope this is helping

vuetify rule function - how to access component label during validation?

I would like to access the label property of a component in it's "Rules" function so I can return an (already) localized field name in the error message.
Is there any way to access the properties of the component in the rule function that's called by Vuetify for validation?
<v-text-field
v-model="obj.count"
:counter="10"
:label="this.$locale.get('WidgetCount')"
:rules="MyRuleFunctionInMyRuleLibrary()"
name="count"
required
></v-text-field>
As can be seen in the code I have a function to localize the field label already, I don't want to re-do it twice or have to specify it twice. In "MyRuleFuctionInMyRuleLibrary" I want to validate the rule and report on it localized properly.
I know I can just pass the localized text Key in my rule function but that would create a redundancy as I would have to type it twice in the template and I also need some other properties of the control / component so I would rather pass or have access to the component itself. I already tried passing "this" to the component, e.g.:
:rules="MyRuleFunctionInMyRuleLibrary(this, obj.count)"
However this in this case appears to be everything on the page / form, not the single component itself.
Using typescript:
<v-text-field v-model="volume.sizePerInstance" :rules="sizePerInstanceRules" :label="$t('volumes.sizePerInstance') + ' (GB)'" type="number" step="0.01" required min="0" color="#0cc2aa"></v-text-field>
You have to define a getter in order to get acces to component properties:
get sizePerInstanceRules() {
return [
(v: number) => v && v > 0 || 'Max size must be greater than 0',
(v: any) => v && !isNaN(v) || 'Max size must be a number',
(v: number) => {
return this.maxValue >= v || 'Exceeded limit';
},
];
}
In Vuetify source code, rules function has only 1 parameter (value). You can work around by define label as data or computed property:
<v-text-field
v-model="obj.count"
:counter="10"
:label="label.count"
:rules="MyRuleFunctionInMyRuleLibrary()"
name="count"
required
></v-text-field>
Add label to data
data: () => ({
label: {
count: this.$locale.get('WidgetCount')
}
})
then you can access localize label in validation function by this.label.count
You might want to watch locale change to change label manually:
watch: {
locale: function () {
this.label = {
count: this.$locale.get('WidgetCount')
}
}
}