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);
},
},
};
Related
Hi currently I am making a add & minus function and the problem I am facing is once I clicked add or minus, the value still remain the same... I use the watch property also and it still not working
The get_list is calling from a API
<b-row v-for="(data,i) in get_list" :key="i" class="mt-3 mx-0">>
<b-button pill
class="quantity-btn btn-secondary"
#click="addQuantity(data.id_product,data.id_style)"
>
<span style="transform: translateY(-5%);">+</span>
</b-button>
<div class="px-2">
<b-form-input type="number" min="1" :max="data.quantity_available" :value="quantity" v-model="data.cart_quantity" class="quantity-input"></b-form-input>
</div>
<b-button pill
class="quantity-btn btn-secondary"
#click="minusQuantity(data.id_product,data.id_style)"
>
<span style="transform: translateY(-5%);">-</span>
</b-button>
</b-row>
script
data: () => ({
get_list: [],
quantity:0,
}),
method :{
addQuantity(id,style) {
this.quantity++
this.updateQuantity(id,style)
},
minusQuantity(id,style) {
this.quantity--
this.updateQuantity(id,style)
},
},
watch: {
quantity(newVal, oldVal) {
console.log('new'+newVal)
}
}
Take a look of this code in code proved above.
<div class="px-2">
<b-form-input type="number" min="1" :max="data.quantity_available" :value="quantity" v-model="data.cart_quantity" class="quantity-input"></b-form-input>
</div>
Here it seems cart_quantity is getting used in template instead of quantity due to this it's not getting updated.
to fix this issue, you can update the cart_quantity property of the data object instead of the quantity data property in the addQuantity and minusQuantity
methods: {
addQuantity(id, style) {
this.get_list.forEach(item => {
if (item.id_product === id && item.id_style === style) {
item.cart_quantity++;
}
});
},
minusQuantity(id, style) {
this.get_list.forEach(item => {
if (item.id_product === id && item.id_style === style) {
item.cart_quantity--;
}
});
}
}
Please remove watch property as well it seems not required.
The problem is that you are performing the add/minus operation on the quantity variable but in the template, you are using the cart_quantity variable which is not increasing/decreasing.
So, either use quantity or cart_quantity as per your logic.
addQuantity(id, style) {
this.cart_quantity++;
// If you are using quantity as v-model
// this.quantity++
this.updateQuantity(id, style);
},
minusQuantity(id, style) {
this.cart_quantity--;
// If you are using quantity as v-model
// this.quantity--
this.updateQuantity(id, style);
},
One more thing, you don't need to use value and v-model together at the same time. If you want to give some initial value to the input, simply assign that value to your cart_quantity on mounted and use it further.
The last thing, you don't need a watcher as well.
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!
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
I have the following code:
<template>
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-outline-dark active">
<input type="radio" name="grp" v-model="channel" value="vh1" checked> VH1
</label>
<label class="btn btn-outline-dark">
<input type="radio" name="grp" v-model="channel" value="mtv"> MTV
</label>
</div>
</template>
<script>
export default {
data() {
return {
channel: 'mtv'
}
},
watch: {
channel: function(newValue) {
console.log('value has been changed to ' + newValue);
}
}
}
</script>
When I click on a radio button, nothing happens. But when I removed the "data-toggle="buttons" attribute for the styling, then it starts working! Can anyone help me find a work around here?
Edit
For those of you who don't know, data-toggle is from bootstrap button plugin, it adds extra styles and functionality to the buttons you are making. Check out the docs here:
https://getbootstrap.com/docs/4.0/components/buttons/#toggle-states
Remove data-toggle and set active class by :class="{ 'active': channel === [value] }" for each input.
Finally got the solution
After long hours of searching, I was able to come up with a way to hack into the buttons.
Step 1
Change the above template to the following (removed v-model from the inputs)
<template>
<div class="btn-group" data-toggle="buttons" v-radio="channel">
<label class="btn btn-outline-dark active">
<input type="radio" name="grp" value="vh1" checked> VH1
</label>
<label class="btn btn-outline-dark">
<input type="radio" name="grp" value="mtv"> MTV
</label>
</div>
</template>
This solution was pretty difficult to find, and the solution was pretty hacky, but it got the job done.
Step 2 (UPDATED 6/18/2018)
Create a directive
In my case, since I was using single file components, I needed to bring it directives in the following way:
radio.js
export default {
inserted: function (el, binding) {
var btns = $(el).find('.btn');
var radioGrpName = $(btns[0]).find('input')[0].name;
$("input[name='" + radioGrpName + "'][value='" + binding.value + "']").closest('.btn').button('toggle');
},
bind: function (el, binding, vnode) {
var btns = $(el).find('.btn');
btns.each(function () {
$(this).on('click', function () {
var v = $(this).find('input').get(0).value;
(function set(obj, str, val) {
str = str.split('.');
while (str.length > 1) {
obj = obj[str.shift()];
}
return obj[str.shift()] = val;
})(vnode.context, binding.expression, v);
})
})
}
}
What this does is it binds a jquery click event to each radio button found in the div containing the v-radio. The second part where I'm doing function set (copied from the answer in reference), it checks to see if there's any dots in the expression, otherwise it sets the value of vnode.context["channel"], which updates the model. The bind hooks up to events before the component is loaded so it can be ready to fire off the internal workings of the radio button.
The inserted function is called after bind, during the physical insertion of the component into the parent node. So when you set an initial state (no event fired), the component will automatically reflect the value set in data.
Step 3
add directives radio to the script
import radio from '../../directives/radio'
<script>
export default {
directives: {
radio: radio
},
data() {
return {
channel: 'mtv'
}
},
//you can use computed property, but you need get and set
watch: {
channel: function(newValue) {
console.log('value has been changed to ' + newValue);
}
}
}
</script>
Using these links as references:
Old custom directive hack using vue 1
Update model from custom directive
I tried to stay away from the Vue components of Spark as much as possible but I discovered I had to implement a certain mail settings so I can't hold it much longer.
Luckily the Spark documentation contains a small cookbook for adding profile fields:
https://spark.laravel.com/docs/4.0/adding-profile-fields
Most parts are within my (limited PHP) comfort zone:
First the blade php:
Mail settings
<div class="col-md-6">
<label class="radio-inline"><input type="radio" value="profile" v-model="form.type" name="profile">Profile</label>
<label class="radio-inline"><input type="radio" value="website" v-model="form.type" name="website">Website</label>
<label class="radio-inline"><input type="radio" value="combined" v-model="form.type" name="combined">Combined</label>
<span class="help-block" v-show="form.errors.has('mail-settings')">
#{{ form.errors.get('mail-settings') }}
</span>
</div>
</div>
Which is integrated:
<!-- Update Mail settings -->
#include('settings.profile.update-mail-settings')
So as can be seen in the previous code block, I wish to store the result of 3 radio buttons.
However the linked Vue js file is giving my headaches:
Vue.component('update-mail-settings', {
props: ['user'],
data() {
return {
form: new SparkForm({
profile: ''
website: ''
combined: ''
})
};
},
mounted() {
this.form.mailsettings = this.user.mailsettings;
},
methods: {
update() {
Spark.put('/settings/profile/mail-settings', this.form)
.then(response => {
Bus.$emit('updateUser');
});
}
}
});
But how on earth do I integrate the radio buttons in the SparkForm?
In Vue, data binding occurs when you v-model to the object by name. Or in other words, you call v-model="object.property" on an input. When the user fills out the form, the value of form.type will match the form input. So simply change your form object to read:
data() {
return {
form: new SparkForm({
type: '' <- this can now be v-modeled to "form.type"
})
};
},
Your radio buttons don't need to change because they are bound correctly: v-model="form.type"
https://v2.vuejs.org/v2/guide/forms.html#Radio