change data variable when passed into a method in vue - vue.js

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

Related

Changing one Vuejs v-model value is changing all other values

I have a Nuxtjs/Vuejs application within which I am creating multiple Nodes. These Nodes have the Radio button for which I have assigned v-model. However, when I change the value of one Vuejs v-model is affecting all other Node Values. Following is the code sample that I have created for the Node. The ID value is unique for each Node.
<template>
<div ref="el">
<div class="header">
Node: {{ ID }}
</div>
<div>
Syntax:
<input
id="identifierTypeURN"
v-model="identifierSyntax"
type="radio"
value="URN"
name="instanceIdentifierURN"
#change="instanceIdentifiersSyntaxChange('URN')"
>
<label for="identifierTypeURN">URN</label>
<input
id="identifierTypeWebURI"
v-model="identifierSyntax"
type="radio"
value="WebURI"
name="instanceIdentifierWebURI"
#change="instanceIdentifiersSyntaxChange('WebURI')"
>
<label for="identifierTypeWebURI">WebURI</label>
</div>
</div>
</template>
I am aware that this is happening because I am using the same v-model name for all the Nodes so I changed to something like this. But still the issue persists:
<template>
<div ref="el">
<div class="header">
Identifiers
Node: {{ ID }}
</div>
<div>
Syntax:
<div v-for="node in allNodeInfo" :key="node.identifiersId">
<div v-if="node.identifiersId === ID">
<input
id="identifierTypeURN"
v-model="node.identifierSyntax"
type="radio"
value="URN"
name="instanceIdentifierURN"
#change="instanceIdentifiersSyntaxChange('URN')"
>
<label for="identifierTypeURN">URN</label>
<input
id="identifierTypeWebURI"
v-model="node.identifierSyntax"
type="radio"
value="WebURI"
name="instanceIdentifierWebURI"
#change="instanceIdentifiersSyntaxChange('WebURI')"
>
<label for="identifierTypeWebURI">WebURI</label>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
ID: '',
nodeId: '',
eventCount: '',
bizStep: '',
allNodeInfo: [],
instanceIdentifierSyntax: ''
}
},
mounted () {
this.$nextTick(() => {
const id = this.$el.parentElement.parentElement.id
const data = this.$df.getNodeFromId(id.slice(5))
this.ID = data.data.ID
this.nodeId = data.data.nodeId
this.allNodeInfo = JSON.parse(JSON.stringify(this.$store.state.modules.ConfigureIdentifiersInfoStore.identifiersArray, null, 4))
const identifiersNode = this.allNodeInfo.find(node => node.identifiersId === this.nodeId)
this.instanceIdentifierSyntax = identifiersNode.identifierSyntax
console.log(JSON.stringify(this.allNodeInfo, null, 4))
})
},
methods: {
// On change of the IdentifierSyntax change, change the value in the respective node info
instanceIdentifiersSyntaxChange (syntaxValue) {
// Change the value of the respective syntax within the Node information in IdentifiersNode array
console.log(this.ID + " --- " + syntaxValue)
}
}
}
</script>
<style>
</style>
I know I am making some small mistake where I need to differentiate each Nodes V-model but nothing is clicking me. Can someone please help me.
You have to pass your node and your v-model also to your methods within this event.. like this, because for every loop in your v-for there will be a "new created node" which includes your v-model and than you can refer to this:
#change="instanceIdentifiersSyntaxChange('URN', node, identifierSyntax)"
In your methods you just do everything based on your node.identifierSyntax = HERE WHAT YOU WANT..
Hopefully I understood the question correct and helped you out!
EDIT: (Standard procedure)
Normally it looks like this when you add a single "input" to your v-for.
Template:
<div v-for="node in inputs" :key="node.id">
<input v-model="node.someDefinition" :value="node.someDefinition"/>
<div #change="another_Function(node, someDefinition)></div>
</div>
<button #click="add_new_Input>ADD</button>
Script:
data() {
return {
id: 0,
inputs: [{ //this is representing the first input when side will be loaded
id: this.id,
//your stuff in here as well
}]
}
}
methods: {
add_new_Input() {
this.inputs.push({
id: this.id += 1
})
}
}
This should be enough.. So in your template you have a v-for where your are looping over an array (or something else but in my case it's an array) with all inputs - be aware every input of me gets an unique ID when it will be created or added.
Also you have - in my case here - an input-tag where you can get the v-model or set the :value. It's binded to my unique node which I have created in my methods.
If you pass your #change you also have to pass the current node and the v-model / value that the correct one will be changed.
Hopefully it now helps you out!

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
}
}

Vue input event not capturing entire field

I have a vue component that adds a search bar and search bar functionality. It contains this line:
<input class="input" type="text" placeholder="Address" v-model="searchQuery" v-on:input="(event) => this.$emit('queryChange', event)">
This captures the text in the search bar and emits it.
In my vue, this triggers my updateSearchQuery function:
this.searchQuery = event.data which merely saves the users input in the searchQuery property in my vue. Everything works fine when I do this, until, I make a search and then, make another call using the same this.searchQuery data.
For example, I'm trying to filter results with the search query '956'. I enter it and this call is made: GET /users?cp=1&pp=20&se=956, just like it should. Then after the page loads, if I go to page 2 of the results, this is the call that is made to the server: GET /users?cp=2&pp=20&se=6. Instead of saving 956 as the queryStr in the the view, it only saves the most recent character entered, instead of the entire content of the serch text.
This happens every time I type in multiple characters as a search query, and then make another call to the server using the unchanged this.searchQuery variable. If my initial search query is only a single character, it works just fine.
What am I doing wrong here? How can I emit the entirety of the text in the search bar, after any change, so that I can always save the whole search query, instead of the just the most recent change?
EDIT: I've add some more code below so the data flow is easier to follow:
Here is the template and script for the search component:
<template>
<div class="level-item">
<div class="field has-addons">
<div class="control">
<input class="input" type="text" placeholder="Address" v-model.lazy="searchQuery" v-on:input="(event) => this.$emit('queryChange', event)">
</div>
<div class="control">
<div class="button is-light" #click="clearInput">
<span class="icon is-small">
<i class="fa fa-times" style="color:#ffaaaa"></i>
</span>
</div>
</div>
<div class="control">
<button class="button is-info" #click="onSearch(searchQuery)">Search</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Search',
props: {onSearch: Function},
data () {
return {
searchQuery: ''
}
},
watch: {},
methods: {
clearInput () {
this.searchQuery = ''
}
}
}
</script>
the emitted queryChange event is caught and listened to in the vue page:
<Search :onSearch="onSearch" v-on:queryChange="updateSearchQuery"> and this triggers the updateSearchQuery function:
updateSearchQuery (event) {
this.searchQuery = event.data
console.log(event.data + ' || event.data')
console.log(this.searchQuery + ' || this.searchQuery')
}
Theoretically, the searchQuery data in my vue should be a copy of the searchQuery data in my component, which is itself merely a copy of whatever the user has input in the search bar.
Then when I make a call to the server I'm using the value in this.searchQuery in my vue:
onSearch (search) {
this.makeServerQuery(1, search)
},
onPaginate (page) {
this.makeServerQuery(page, this.searchQuery)
},
makeServerQuery (page = null, search = null) {
let queryStr = ''
if (page !== null) {
queryStr += '?cp=' + page + '&pp=' + this.perPage
}
if (this.searchQuery !== '') {
queryStr += '&se=' + this.searchQuery
} .....
The on onSearch(search) function is called whenever the search button is pressed. That seems to work fine, because when the button is pressed the entire searchQuery is passed, not just the last change.
An input event's data value appears to be the last typed character, and not the current value of the input. A simple fix is:
#input="$emit('queryChange', searchQuery)"
This works because the model will always be updated before the input event handler runs.
Here's a complete working component example:
<input
v-model="searchQuery"
type="text"
placeholder="Address"
#input="onInput"
/>
export default {
data() {
return { searchQuery: '' };
},
methods: {
onInput() {
console.log(this.searchQuery);
this.$emit('queryChange', this.searchQuery);
},
},
};

Is there anyway to set a different value for the "input" than the vue variable?

I need to change the display of a input depending on the value returned from the db for this variable: paragraph.RCBulletinNumber
I have tried the computed method, but I think this is not what I need
computed: {
formatRCBulletinNumber: {
set (val){
return this.paragraph.RCBulletinNumber;
}
}
},
This is my input declaration using v-model='paragraph.RCBulletinNumber':
<div class="form-group">
<div v-if='typeof paragraph != undefined /*/<!--&& parseInt(paragraph.RCBulletinNumber)>0 -->/*/'>
<input type="text" style="width: 40%;" class='form-control' id="RCNumber" placeholder="RC Number" v-model='paragraph.RCBulletinNumber'>
</div>
</div>
What I expect is that if the value of paragraph.RCBulletinNumber is less than or equal to 0 the input remains empty. Right now if the variable equals 0 the input is 0
But if the paragraph.RCBulletinNumber is equal to 0, the value must go to the database again, my goal is just to change the value of the input to be more userfriendly.
Simply define the getter and setter of the computed property:
computed: {
formatRCBulletinNumber: {
// getter
get: function () {
return this.paragraph.RCBulletinNumber || '';
},
// setter
set: function (newValue) {
this.paragraph.RCBulletinNumber = newValue;
}
}
}
And then reference the computed property on the input:
<input type="text" style="width: 40%;" class='form-control' id="RCNumber" placeholder="RC Number" v-model='formatRCBulletinNumber'>
reference: https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter

Is it possible to bind a class to an element based on a boolean expression?

I want to bind a class to an element based on the result of a boolean expression. For example:
<input type="email" :class="{ invalid: submitted && $v.email.$error }">
But the "invalid" class is not added to the element if I evaluate both conditions; it only works when I evaluate one or the other, for example:
<input type="email" :class="{ invalid: submitted }">
or
<input type="email" :class="{ invalid: $v.email.$error }">
work just fine. I realize I could use computed properties for this, but I'd have to create a computed property for each field in my web form and that seems redundant. Is there a better way?
<input type="email" :class="{ invalid: formValid($v.email.$error) }">
computed: {
formValid(){
return (val) {
val && this.submitted ? true : false
}
}
}
Could you test this out im curious if it would work, then you just have to pass a param and need one Computed.
Your Code
<input type="email" :class="{ invalid: submitted && $v.email.$error }">
The problem here, even though you defined $v in your local state, Vue is unable to find it. Try to define your local state in your data property without a preceding dollar sign. Because $ carries an extra meaning in Vue.
Generally $ means instance properties like data, el, root, children etc.
For example to access what element your Vue instance is mounted on, you can use this.$el.
So you can modify your code like this -
<input type="email" :class="{ invalid: submitted && $data.$v.email.$error }">
I think I've found a pretty good solution. I used a method with an argument instead of computed properties:
<template>
<form #submit.prevent="onSubmit" novalidate>
<input
type="email"
:class="{ invalid: isInvalid($v.email.$error) }"
v-model.lazy="email">
<button type="submit">Submit</button>
</form>
</template>
<script>
import { required, email } from 'vuelidate/lib/validators'
export default {
data () {
return {
email: '',
submitted: false
}
},
validations: {
email: {
required,
email
},
},
methods: {
isInvalid (val) {
return val && this.submitted
},
onSubmit () {
this.submitted = true
if (!this.$v.$invalid) {
// do something with the email address
}
}
}
}
</script>