Updating v-model data in form inputs with a series of methods in a Vuetify project? - vue.js

I am attempting to update a series of v-text-fields (type='number') so that after the user has entered in a numeric value, the number shown in the input will be updated with commas (so a value of 5032 would become 5,032 for example). I found this article and was able to accomplish what I'm after with a single input using the example provided...
Markup:
<div id="app">
<div v-if="visible === true">
Enter Amount: <br>
<input type="number"
v-model="amount"
placeholder="Enter Amount"
#blur="onBlurNumber"/>
</div>
<div v-if="visible === false">
Enter Amount: <br>
<input type="text"
v-model="amount"
placeholder="Enter Amount"
#focus="onFocusText"/>
</div>
Script:
data: {
amount: null,
temp: null,
visible: true
},
methods: {
onBlurNumber() {
this.visible = false;
this.temp = this.amount;
this.amount = this.thousandSeprator(this.amount);
},
onFocusText() {
this.visible = true;
this.amount = this.temp;
},
thousandSeprator(amount) {
if (amount !== '' || amount !== undefined || amount !== 0 || amount !== '0' || amount !== null) {
return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} else {
return amount;
}
}
}
...but I want to make the methods generic enough to work with any numeric v-text-fields I am using. I have been able to update a parameter value within my methods, but have been unable to actually update the v-model data of the v-text-field.
Markup:
<div id="app">
<div v-if="visible === true">
<v-text-field
class="mb-3 d-inline-block"
type="number"
prepend-inner-icon="attach_money"
v-model="amount"
label="Amount"
mask="##########"
outline
:rules="[v => !!v || 'Amount is required']"
#blur="onBlurNumber(amount)"
required>
</v-text-field>
</div>
<div v-if="visible === false">
<v-text-field
class="mb-3 d-inline-block"
prepend-inner-icon="attach_money"
v-model="amount"
label="Amount"
outline
#focus="onFocusText(amount)"
>
</v-text-field>
</div>
Script:
onBlurNumber(data) {
this.visible = false;
this.temp = data;
data = this.thousandSeprator(data);
},
onFocusText(data) {
this.visible = true;
data = this.temp;
},
I can log the value of data in these methods and confirm that the commas are being applied correctly, but now I don't know how to send the data value back to update the v-text-field's v-model. I experimented with selecting the v-text-field using a ref value but the ref turns up as an undefined value when the method is triggered.
Does anyone know how I can update the v-model of the v-text-field using arguments in this sort of fashion so the methods are reusable?

I assume that you have multiple data items for each of the text fields:
data: function() {
return {
// Field 1
temp1: null,
amount1: null,
visible1: true,
// Field 2
temp2: null,
amount2: null,
visible2: true
}
}
In your markup, when calling the method you could then pass the name of the property, or maybe its suffix.
<v-text-field #blur="onBlurNumber('2')"
And in your script, you could update the data items by using dynamic properties:
methods: {
onBlurNumber(suffix) {
this["visible" + suffix] = false;
this["temp" + suffix] = this["amount" + suffix];
this["amount" + suffix] = this.thousandSeprator(this["amount" + suffix]);
},
Here's a working example of two independent text inputs that are calling the same methods to achieve this. We could refactor this to reduce the number of data items using arrays if needed.

Related

format input value as you type

I have a couple of inputs in which I can enter an amount, I am trying to write in the inputs to format the amount and add the points(.) of thousands, millions, etc.
The value that is written in the input is saved in an object to send it later. It had a function that did this, but when formatting the amount, the value was formatted the same way adding the points.
How can I format only when writing the amount in the input and it is formatted, but the value remains the same?
These are the inputs that I want to format when writing:
<div class="budget info" :class="{ 'input-error' : inputsValid.budget }">
<label>Budget</label>
<input
type="number"
v-model="info.budget"
#input="() => { info.dealbudget === '' ? inputsValid.budget = true : inputsValid.budget = false }"
>
</div>
<div class="area info" :class="{ 'input-error': inputsValid.area }">
<label>Area</label>
<input
type="text"
v-model="info.area"
#input="() => { info.superficie === '' ? inputsValid.area= true : inputsValid.area= false }">
</div>
object where I store the value:
const info = ref({
budget: '',
area: '',
})
I am dealing with a computed one, I think that is the way to do it, when loading it formats the amount ($0.00) well, when writing it does not:
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
})
const format = computed(() => {
return formatter.format(info.value.dudget)
})

change data variable when passed into a method in vue

I have the following vue component where I am changing the class of the parent row based on whether or not an input is focused
<template>
<div class="form form--login">
<div class="form__row" :class="{entered: emailEntered}">
<label class="form__label" for="login-form-email">Email address</label>
<input type="text" class="form__control form__control--textbox" name="email-address" id="login-form-email" #focus="emailEntered = true" #blur="handleBlur($event, emailEntered)">
</div>
<div class="form__row" :class="{entered: passwordEntered}">
<label class="form__label" for="login-form-password">Password</label>
<input type="password" class="form__control form__control--textbox form__control--password" name="password" id="login-form-password" #focus="passwordEntered = true" #blur="handleBlur($event, passwordEntered)">
</div>
</div>
</template>
<script>
export default {
name: 'login-form',
data() {
return {
emailEntered: false,
passwordEntered: false,
}
},
methods: {
handleBlur(e, enteredBool) {
if (e.currentTarget.value.trim() === '') {
// this doesn't do anything - I can do an if else statement to change this.passwordEntered or this.emailEntered based on the name of the current target, but how do I change the value by passing it into the method?
enteredBool = false;
}
},
}
}
</script>
but it doesn't seem to change the variable that is passed into the method - how do I pass a data variable into the method and change it's value? Or should I be doing it in a different way? I don't really want to be doing an if else statement as I may have a form that has got a lot more inputs on and I think that would be really inefficient to maintain
I also thought that I could do something in the #bur as you can do #blur="passwordEntered = false", but I wasn't sure how to check if the field was empty or not
In order to change the variable, you need to refer it using this
handleBlur(e, enteredBool) {
if (e.currentTarget.value.trim() === '') {
this[enteredBool] = false; //Change added
}
},
and the way you pass it should be like
#blur="handleBlur($event, 'emailEntered')" //Added single quotes
and
#blur="handleBlur($event, 'passwordEntered')" //Added single quotes

Dynamic Rendering with V-If

I am creating a form and I want to show certain fields only when a certain button is pressed, but take those fields away and show other fields when another button is pressed.
I am new to vue (and coding), but I think I'm wanting to use v-if, but I can't seem to return the values back to the v-if field.
If the type MAGAZINE is selected, then a method sets the typeIsMagazine to TRUE and the other typeselectors to FALSE. I would expect that once typeIsMagazine is set to true, then the v-if would be triggered and the form fields will be shown.
The method is being triggered, and I am testing it with console.log so I know the if functions are working. I just don't think it's being returned to v-if.
<template>
<form #submit.prevent="handleSubmit">
<label class="main">Type:</label>
<div class="type-row">
<div class="sub-column">
<div
class="sub"
#click="updateType('auto_stories')"
:class="{ selected: type === 'auto_stories' }"
>
Book
</div>
</div>
<div class="sub-column">
<div
class="sub"
#click="updateType('article')"
:class="{ selected: type === 'article' }"
>
Article
</div>
</div>
<div class="sub-column">
<div
class="sub"
#click="updateType('website')"
:class="{ selected: type === 'website' }"
>
Website
</div>
</div>
</div>
<template v-if="typeIsWebsite">
<label class="main">Website:</label>
<input type="text" class="text" v-model="url" required />
</template>
<template v-if="typeIsArticle">
<label class="main">Magazine:</label>
<input type="text" class="text" v-model="magazine" required />
</template>
<button class="form">Add Entry</button>
</form>
</template>
<script>
export default {
data() {
return {
typeIsWebsite: false,
typeIsArticle: false,
typeIsBook: false,
};
},
methods: {
updateType(typeSelect) {
this.type = typeSelect;
let typeIsWebsite = false;
let typeIsBook = false;
let typeIsArticle = false;
if (typeSelect === "website") {
typeIsWebsite = true;
typeIsArticle = false;
typeIsBook = false;
} else if (typeSelect === "article") {
typeIsWebsite = false;
typeIsArticle = true;
typeIsBook = false;
} else if (typeSelect === "auto_stories") {
typeIsWebsite = false;
typeIsArticle = false;
typeIsBook = true;
}
return typeIsWebsite, typeIsArticle, typeIsBook;
},
In updateType, your variables typeIsWebsite, typeIsBook, etc are declared as local variables using let. Thus, when you do the if, you are updating local variables, not your component's instance variables.
To fix, remove the typeIsX variable declarations in updateType, and use this.typeIsX to refer to each variable.
Like so:
updateType(typeSelect) {
this.type = typeSelect;
if (typeSelect === "website") {
this.typeIsWebsite = true;
this.typeIsArticle = false;
this.typeIsBook = false;
} else if (typeSelect === "article") {
this.typeIsWebsite = false;
this.typeIsArticle = true;
this.typeIsBook = false;
} else if (typeSelect === "auto_stories") {
this.typeIsWebsite = false;
this.typeIsArticle = false;
this.typeIsBook = true;
}
}
Finally, the function doesn't need to return anything.
As an extra advice, note that this is really verbose code and at least in your use case you don't need all the flags. Just keeping the current type as a string and then comparing against that would be enough. For example:
this.typeIsWebsite is equivalent to this.type === 'website'.
Remember: less code means less errors!

Vue Vuelidate to validate unique value based on data from server

I am trying to create a form with vuelidate. In one field I would like to check if the name is taken or not. I have some async methods to get names and ids from the server and assigning them to arrays, and I have a custom validator that checks if the name exists, either by checking the includes in the array, or by checking a computed value that already checks the array.
Neither of the methods seems to work however. If I check the array, its seems to be empty since it always returns false (even tho the array has values according to the vue tools in the browser). If I check the the computed value, I get an error with undefined.
So my question is, what is the simplest why to validate whether a value exists, and why isn't my current code wokring?
<template>
<div>
<form class="needs-validation" #submit.prevent="submitForm" method="post">
<div class="form-group row">
<label class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10">
<input
type="text"
class="form-control"
:class="{ 'is-invalid': $v.form.name.$error }"
id="name"
placeholder="enter name"
v-model="form.name">
<span
class="text-danger"
v-if="!$v.form.name.required && $v.form.name.$dirty">name is required</span>
<span
class="text-danger"
v-if="!$v.form.name.isUnique && $v.form.name.$dirty">name not unique</span>
</div>
</div>
</form>
</div>
</template>
<script>
import { required } from 'vuelidate/lib/validators'
const isUnique = (value, vm) => {
if (value === '') return true
if (vm.names) return !vm.names.includes(value)
return true
}
export default {
data() {
return {
form: {
name: ""
}
ids: [],
names: []
}
}
validations: {
form: {
name: {
required,
isUnique
}
}
}
async created() {
try {
const response = await this.$http.get('/get_data/?fields=id,name')
var array_id = []
var array_name = []
for (var data of response.data) {
array_id.push(data['id'])
array_name.push(data['name'])
}
this.ids = array_id
this.names = array_name
}
}
}
<script>
Seem like you miss the right way to write down methods
form: {
name: {
required,
isUnique : this.isUnique
}
}
},
methods: {
isUnique = (value, vm) => {
if (value === '') return true
if (vm.names) return !vm.names.includes(value)
return true
}
}

How to update or modify any value in vuetifyjs text-field?

I'm a newbie with vuejs.
I'm trying to create some custom number keyboard like:
Here is my HTML code
<input type="text" #click="keyboard($event)" v-model="box.height"/>
<input type="text" #click="keyboard($event)" v-model="box.width"/>
<input type="text" #click="keyboard($event)" v-model="box.length"/>
<input type="text" #click="keyboard($event)" v-model="box.weight"/>
Methods
export default {
data: () => ({
active: null,
box: {
height: '2.0'
width: '3.0',
length: '2.5',
weight: '0.5'
}
}),
methods: {
keyboardValue(keyboardValue) {
var latestValue = ''
if(this.active !== null){
latestValue = this.active.target.value += keyboardValue
this.active.target.value = latestValue;
this.active.target.focus()
}
},
keyboard($event){
this.active = $event
},
}
}
I achieved as expected with input HTML. GIF image is what I achieved but with vuetify I can't update or modify any value.
Here are the vuetify component
<v-text-field label="Height" #click="keyboard($event)" v-model="box.height"></v-text-field>
<v-text-field label="Width" #click="keyboard($event)" v-model="box.width"></v-text-field>
<v-text-field label="Length" #click="keyboard($event)" v-model="box.length"></v-text-field>
<v-text-field label="Weight" #click="keyboard($event)" v-model="box.weight"></v-text-field>
Define #click="" and update active text field value. Make it work as a virtual keyboard. How can I get or update any value?
Here is the codepen link
Demo
Thanks.
I think it needs to be tied to the v-model of the text-fields. Assign each text-field a data var.
<v-text-field label="Height" #click="keyboard('h')" v-model="h"></v-text-field>
<v-text-field label="Width" #click="keyboard('w')" v-model="w"></v-text-field>
<v-text-field label="Length" #click="keyboard('l')" v-model="l"></v-text-field>
Then set the active data var and update...
methods: {
keyboardValue(keyboardValue) {
if (this.active !== null) {
this[this.active] = (this[this.active]||'') + keyboardValue
}
},
keyboard(model){
this.active = model
},
}
Demo