How do you access v-model input inside method? - vue.js

I'm very new to Vue.js and I can't figure out how to pass an input value to a method upon hitting enter so I can make an Axios request with it? I've tried the following but am getting "searchTerm is not defined".
Template:
<input v-on:keyup="hitEnter" v-model="searchTerm" type="text"/>
Data:
data () {
return {
searchTerm: ''
}
}
Method:
hitEnter (e) {
if (e.keyCode === 13) {
console.log('Enter was hit' + searchTerm);
var vm = this;
axios.get('/api/search?q=' + searchTerm)
.then(function (response) {
vm.results = response.data.data;
});
}
}
Thanks in advance for any assistance on this.

Related

How do I use Vuelidate with a custom validator?

I’m trying to validate a 6 digit code with vuelidate. If this.validationcode equals false I want to show a validation error. I'm pretty new to vue so I'm not entirely sure where I'm going wrong. How do I get this to work?
The error I get is:
TypeError: Cannot read property '__isVuelidateAsyncVm' of undefined
JS
data() {
return {
validationcode: false,
validationRules: [
{ vcode: {
required,
numeric,
minLength: minLength(6),
maxLength: maxLength(6),
validCode () { return this.validationcode }
} },
],
};
},
I also tried it as an arrow function but it doesn't get the value properly from the looks of it.
validCode: () => {
console.log("the val code is " + this.validationcode)
return this.validationcode
}
HTML - v.formData.vcode.validCode - In the current front end view, this rule is triggered every time.
<div class=“form-group”>
<input
type=“text”
class=“form-control”
:class=“hasError(‘vcode’) ? ‘is-invalid’ : ‘’”
placeholder=“Enter your 6 digit code”
v-model=“formData.vcode”
/>
<div v-if=“hasError(‘vcode’)” class=“invalid-feedback”>
<div class=“error” v-if=“!$v.formData.vcode.required || !$v.formData.vcode.minLength || !$v.formData.vcode.maxLength”>
Enter your 6 digit code
</div>
<div class=“error” v-if=“!$v.formData.vcode.numeric”>
Should be a valid value.
</div>
<div class=“error” v-if=“!$v.formData.vcode.validCode”>
Code incorrect, please try again.
</div>
</div>
</div>
This is the method that I am assigning true/false to this.validationcode.
verifyNumber() {
var numbers = /^[0-9]+$/;
if (code.match(numbers)) {
// Twilio functions do not accept multipart/form-data
const data = new URLSearchParams();
data.append("phone_number", m.mobileNumber);
data.append("verification_code", code);
fetch("https://saffron-cheetah-1234.twil.io/check", {
method: "POST",
body: data,
})
.then((response) => response.json())
.then((json) => {
if (json.success) {
console.log("Successfully verified the token.");
this.validationcode = true;
} else {
this.validationcode = false;
console.log(this.validationcode);
console.log("Incorrect token.");
}
})
.catch((err) => {
console.log(err);
});
} else {
}
},

Update image src on the fly with VueJS

I'm a new VueJS user, currently struggling with updating image src on the fly. This is what I've got:
Template:
<div v-for="place in places">
<img
v-bind:src="isPlacePrivate(place.data.place_is_private)"
v-on:click="setPlaceAsPrivate(place.data.place_is_private, place.data.place_id)"
>
</div>
<script>
export default {
data: function () {
return {
places: null,
}
},
mounted () {
this.username = this.$route.params.username;
axios.get('/api/' + this.username + '/places')
.then(response => {
this.places = response.data.data;
})
.catch(error => {
// show error
});
},
methods: {
isPlacePrivate: function (value) {
// If Place is private
if (value == 1) {
var src = '/icons/padlock-color.png'
} else {
var src = '/icons/padlock.png'
}
return src;
},
setPlaceAsPrivate: function (value, placeId) {
let data = {
isPrivate: value
};
axios.put('/api/' + this.username + '/places/' + placeId + '/edit', data)
.then(response => {
let newValue = response.data.data.private;
this.isPlacePrivate(newValue);
})
.catch(error => {
// show error
});
},
},
}
</script>
On a page load -> if a particular place is private it will show colored padlock icon or uncolored padlock if a place is public!
A user will be able to press on the padlock icon and change the value from public->private or private->public.
Everything is working fine but the padlock image is not updating on the fly when a user is clicking on it, I need to refresh a page to see changes! How to make it work?
I would suggest using a computed property so that it is reactive
Also according to your updates you are looping through an array of places so when you get your response from your axios call instead of just updating the icon I would try replacing the object in the array so I created the method called updatePlace() and I pass in the response object.
And change your places in the v-for to a computed property as well so that it is also reactive
Template:
<div v-for="place in placesArray" :key="index" v-if="places">
<img
v-bind:src="imgSrc"
v-on:click="setPlaceAsPrivate(place.data.place_is_private, place.data.place_id)"
v-if="imgSrc"
>
</div>
Script:
<script>
export default {
data() {
return {
src: '',
places: null
}
},
computed: {
imgSrc() {
return this.src
},
placesArray() {
return this.places
}
},
Methods: {
isPlacePrivate: function (value) {
// If Place is private
if (value == 1) {
this.src = '/icons/padlock-color.png'
} else {
this.src = '/icons/padlock.png'
}
},
setPlaceAsPrivate: function (value, placeId) {
let data = {
isPrivate: value
};
axios.put('/api/' + this.username + '/places/' + placeId + '/edit', data)
.then(response => {
console.log(response);
let newValue = response.data.data;
this.updatePlace(newValue);
})
.catch(error => {
console.log(error);
});
},
},
updatePlace(newPlace) {
const index = this.places.findIndex(place => place.id === newPlace.id)
this.places.splice(index, 1, place)
},
created() {
this.username = this.$route.params.username;
axios.get('/api/' + this.username + '/places')
.then(response => {
this.places = response.data.data;
})
.catch(error => {
// show error
});
}
}
</script>
Also make sure to move your mounted method to a created() method so that it is called before anything else is trying to render.
Apparently the problem is that you are calling the function and printing its return on the <img v-bind:src>, the isPlacePrivate function returns a value, so when you use this function within the setPlaceAsPrivate it returns the value only in scope of setPlaceAsPrivate.
The isPlacePrivate function does not modify any data value of the component, so the image always remains the same. You just need to set a data and manipulate its value in the isPlacePrivate function.
Template
<img
v-bind:src="bindSrc"
v-on:click="setPlaceAsPrivate(place.data.place_is_private, place.data.place_id)"
>
Script
<script>
export default {
data() {
return {
bindSrc: '/icons/padlock-color.png', // default img src value
... // your other values
}
},
Methods: {
isPlacePrivate: function (value) {
// If Place is private
if (value == 1) {
this.bindSrc = '/icons/padlock-color.png'
} else {
this.bindSrc = '/icons/padlock.png'
}
},
setPlaceAsPrivate: function (value, placeId) {
let data = {
isPrivate: value
};
axios.put('/api/' + this.username + '/places/' + placeId + '/edit', data)
.then(response => {
console.log(response);
let newValue = response.data.data.private;
this.isPlacePrivate(newValue);
})
.catch(error => {
console.log(error);
});
},
}
}
</script>

How to show if following or not Vue Js

I am trying to create a follow button, already wrote the code, so when the user follows, it goes straight to the database, the user id and the followed user id, in the component, i wrote a code that check if the user is already being followed to return true and false and then if following is true should display following and vice versa, but so far whenever the page loads it shows followed when true , if i go to another page and come back, it go backs to showing follow which is false, till i refresh the entire page again
<template>
<button v-bind:class="{followVendor:!following,followingVendor:following}"
type="button"
#click="followToggle"
class=" btn btn-sm btn-outline">
{{followText}}</button>
</template>
data:{
userFollowing:{},
following: false,
followText:'follow',
}
mounted(){
axios.get(`/api/user-following`,
{
headers: { Authorization: `Bearer ${this.user.access_token}` }
}).then(
response=>{
if (response.status === 200) {
this.userFollowing = response.data;
this.writer=true;
if (this.userFollowing.length > 0) {
if (this.userFollowing[0].user_id === this.user.id && this.userFollowing[0].vendorId === this.writterD.id) {
this.following = true;
this.followText = 'following';
}
}
}
}
)
}
methods: {
follow(params, name) {
let data = {
vendorName: name,
vendorId: params
};
if (this.auth) {
axios
.post("/api/user-following", JSON.parse(JSON.stringify(data)), {
headers: { Authorization: `Bearer ${this.token}` }
})
.then(response => {
if (response.status === 201) {
this.following = true;
this.followText = 'following';
this.$toasted.success("Successfully followed");
} else if (response.data === " Already following") {
this.$toasted.error("Already following");
} else {
this.$toasted.error("Error");
}
})
.catch(error => {
console.log(error);
});
} else {
this.$toasted.error("You have to log in to follow");
}
},
unFollow(){
let vendor = this.writterD.id;
let unfollow = confirm(`unfollow ${this.writterD.storeName}?`)
if (unfollow) {
axios.delete(`/api/user-following/${vendor}`,
{headers: {Authorization: `Bearer ${this.token}`}}).
then(response=>{
if (response.status === 200) {
this.following = false;
this.followText = 'follow';
this.$toasted.success("Successfully Unfollowed");
}else{
this.$toasted.error("Already Unfollowed");
}
})
}
},
followToggle(){
this.following? this.unFollow(): this.follow(this.writterD.id, this.writterD.storeName);
},
}
how can i make it always show the right one even if i don't reload the entire page please
What you need to do is force a rerender, the best way to do this is place a key on the component you need to update and then change the key when the info is updated. When the key is updated the data will update as the component will rerender. Like this:
<button :key="update" v-bind:class="{followVendor:!following,followingVendor:following}"
type="button"
#click="followToggle"
class=" btn btn-sm btn-outline">
{{followText}}</button>
...
data(){
return {
update: 0
}
}
...
this.followText = 'following';
this.update++
See here for a nice article on the topic:
https://michaelnthiessen.com/force-re-render

Vue method return undefined when used in other method

I am trying to fetch replies of a comment in an object.
From the backend,i.e,LARAVEL I receive 1 object. But, in Vue it becomes undefined
Method
fetchReplies(commentid) {
axios
.get("/reply/" + commentid)
.then(res => {
if (res.data != null) {
console.log(res.data);
return res.data;
}
})
.catch(function(err) {
console.log(err);
});
}
OutPut
(2) [{…}, {…}] // for commentid 42
But When used this method in some other method
fetchComments() {
var boardid = this.boardid;
axios
.get("/comment/" + boardid)
.then(res => {
if (res.data != null) {
this.comments = res.data;
console.log(this.fetchReplies(42));
}
})
.catch(function(err) {
console.log(err);
});
},
OutPut
Undefined
Before a while, when i fetch in Vue, I receive 1 object containing data and one with no data. But, suddenly that object with no data disappears.
Your console.log(this.fetchReplies(42)); is calling a function which is still running as axios in asynchronous
If you make your fetchComments an async function, you can wait until your fetchReplies is finished before logging something.
Added a code snippet, make sure axios is returning something as well.
let results = await this.fetchReplies(42)
console.log(results)
const URL = 'https://jsonplaceholder.typicode.com/posts';
new Vue({
el: "#app",
data: {
comments : '',
replies : ''
},
methods: {
fetchReplies(id) {
return new Promise((resolve, reject) => {
axios
.get(URL)
.then(res => {
if (res.data != null) {
resolve(res.data)
} else {
reject('RejectReason')
}
})
.catch(function (err) {
console.log(err);
reject(err)
});
})
},
async fetchComments() {
axios
.get(URL)
.then(async res => {
if (res.data != null) {
//Result from comments is in
this.comments = res.data;
let replies = await this.fetchReplies(1);
this.replies = replies;
}
})
.catch(function (err) {
console.log(err);
});
}
}
})
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="fetchComments()">
Some text
</button>
<h1>Comments</h1>
{{comments}}
<h2>Replies</h2>
{{replies}}
</div>
Update: Change snippet to change data visibly in template
Axios is an asynchronous call, so it seems console.log is called before the fetch call has returned. The most convenient way to use axios call it with es2017 async/await.

Too many requests when controlling spinner show/hide from axois interceptors

I have an SPA written in Vue (Webpack) where I want to control the visibility of a spinner based on whether or not the app is currently handling an HTTP request or a response.
Following some tutorials, I came up with the event bus scheme and did this:
Created eventBus.js:
import Vue from 'vue';
export const eventBus = new Vue();
I'm setting my axios interceptors in the created() hook of App.vue. Here's what the necessary functions look like in that component:
data() {
return {
showLoader: false
};
},
created(){
this.setAxiosInterceptors();
// some code removed //
}
},
mounted() {
eventBus.$on('show-loader', () => {
this.showLoader = true;
});
eventBus.$on('hide-loader', () => {
this.showLoader = false;
});
},
methods: {
setAxiosInterceptors() {
var tokenCookieName = this.$store.getters.getCookieNames.apiToken;
var cookieDefaultValue = this.$store.getters.getCookieDefaultValue;
// token expired middleware
this.axios.interceptors.response.use(response => {
var data = response.data;
if(data.info.api_token) {
this.$cookie.set(tokenCookieName, data.info.api_token);
}
if(data.status == 'error' && data.info.login_failed) {
this.$cookie.set(tokenCookieName, cookieDefaultValue);
window.location = '/'; // not possible to use Vue router here
}
eventBus.$emit('hide-loader');
return response;
},
error => {
eventBus.$emit('hide-loader');
console.log('Response interception failed!');
return Promise.reject(error);
});
// attach API token middleware
this.axios.interceptors.request.use(config => {
var apiToken = this.$cookie.get(tokenCookieName);
if (!apiToken) {
apiToken = cookieDefaultValue;
}
config.headers.Authorization = 'Bearer ' + apiToken;
eventBus.$emit('show-loader');
return config;
},
error => {
eventBus.$emit('hide-loader');
console.log('Request interception failed!');
return Promise.reject(error);
}
);
}
}
Please ignore some of the code that isn't relevant to the problem, but I wanted to show how things are set up. Problem is, as soon as I visit my home page, the app keep making the startup GET requests over and over, until my server returns a 429 error.
Interestingly, in my eventBus.$on handlers, if I just do a console.log, this behavior doesn't appear (of course, the spinner doesn't work as well) but as soon as I change a variable or call a vuex action, this infinite reloading starts.
Any clue?
In the main.js file
Vue.prototype.$axios = axios.create(
{
headers:
{
'Content-Type': 'application/json',
},
baseURL: process.env.API_URL
}
);
Vue.prototype.$axios.interceptors.request.use(
config =>
{
eventBus.$emit('show_spin');
let token = getTokenID();
if(token && token.length) config.headers['Authorization'] = token;
return config;
},
error =>
{
eventBus.$emit('hide_spin');
if (error.status === 401) VueRouter.push('/login');
else throw error;
}
);
Vue.prototype.$axios.interceptors.response.use(
response =>
{
eventBus.$emit('hide_spin');
return response;
},
error =>
{
eventBus.$emit('hide_spin');
return new Promise(function(resolve,reject)
{
if (error.config && error.response && error.response.status === 401 && !error.config.__isRetry)
{
myVue.refreshToken(function()
{
error.config.__isRetry = true;
error.config.headers['Authorization'] = getTokenID();
myVue.$axios(error.config).then(resolve,reject);
},function(flag) // true = invalid session, false = something else
{
if(process.env.NODE_ENV === 'development') console.log('Could not refresh token');
if(getUserID()) myVue.showFailed('Could not refresh the Authorization Token');
reject(flag);
});
}
else throw error;
});
}
);
let myVue = new Vue(
{
el: '#app',
data: function()
{
return {
spin_visible: 0, // dynamically show/hide spinner
};
},
created: function()
{
eventBus.$on('show_spin', this.showSpin);
eventBus.$on('hide_spin', this.hideSpin);
},
methods:
{
showSpin: function()
{
this.spin_visible++;
},
hideSpin: function()
{
if(this.spin_visible>0) this.spin_visible--;
},
....
and then in App.vue
<template>
<router-view/>
<div class="spinner" v-show="$root.spin_visible">
<!-- define your spinner here -->
</div>
</template>