I'm trying to implement modal windows with Vuejs.
The code below shows that after the user uploads the favorite photo,
then modal window appears, and photos which were uploaded so far and the newly registered photos are displayed
it will confirm when the user press the "confirm" button.
However, at present, data is not set in the modal window after fetching data with ajax after uploading.
How do I set the data in the modal window part?
<template>
<div>
<!-- upload -->
<div class="button__action">
<button type="button" #click="uploadData(originalData.image)">upload</button>
</div>
<!-- Modal window -->
<modal name="modal-view">
<div>
<div class="modal__box" v-if="modalList.list">
<img :src="modalList.list.url">
<p class="image__name">{{modalList.list.name}}</p>
</div>
<button type="button" #click="submit">Confirm</button>
</div>
</modal>
</div>
</template>
<script>
import { post } from './handler/api'
import { toFormat } from './handler/form'
export default {
props: {
originalData: {
type: Object,
required: true,
}
},
data: function(){
return {
modalList : {
list : [],
},
}
},
methods: {
showModal () {
this.$modal.show('modal-view');
},
uploadData() {
const form = toFormat({image: this.originalData.image})
post(`/api/upload/`, form)
.then((res) => {
if(res.data) {
Vue.set(this.$data, 'modalList', res.data.list);
this.$modal.show('modal-view');
}
})
.catch((err) => {
//error
})
},
submit() {
}
}
}
</script>
Try this:
uploadData() {
var vm = this;
post(`/api/upload/`, toFormat({image: this.originalData.image})).then(res => {
if(res.data) {
vm.modalList = res.data.list;
this.$modal.show('modal-view');
}
})
}
Related
I'm trying to build an image preview system for an avatar:
<template>
<div>
<div class="level">
<img :src="avatar" width="50" height="50" class="mr-1">
</div>
<form method="POST" enctype="multipart/form-data">
<input type="file" accept="image/*" #change="onChange">
</form>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
avatar: null,
setAuthHeader: axios.defaults.headers.common['Authorization'] = 'Bearer ' + this.$store.state.token,
};
},
computed: {
user() {
return this.$store.state.user
},
avatar_path() {
this.avatar = this.user.avatar_path
},
},
methods: {
onChange(e) {
if (! e.target.files.length) return;
let file = e.target.files[0];
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = e => {
this.avatar = e.target.result;
this.persist(file);
};
},
persist(avatar) {
let data = new FormData();
data.append('avatar', avatar);
this.setAuthHeader;
axios.post(`/settings/avatar`, data)
.then(() => flash('Avatar uploaded!'));
}
}
}
</script>
In my code the user computed property returns a JSON object from vuex which gets the object from local stroage.
What keeps happening is when I refresh the page that changes the profile image the default image doesn't show up. The element looks like this in chrome devtools:
<img width="50" height="50" class="mr-1">
When I open up vue dev tools, click on the component this functionality is in, the image src gets added:
<img width="50" height="50" class="mr-1" src="http://127.0.0.1:8000/storage/avatars/default.png">
You're using the avatar_path computed property incorrectly:
You shouldn't be modifying state in a computed property (you're assigning to this.avatar).
Nothing is accessing avatar_path for it to be called. It's when you open Vue dev tools that the dev tools code accesses that property so it can display it in the component data UI.
The best fix is to change avatar into a computed property like this:
computed: {
avatar() {
if (this.user) {
return this.user.avatar_path;
} else {
// Use a placeholder image URL
return '/path/to/placeholder.png';
}
}
}
<template>
<div>
<div class="level">
<img :src="avatar" width="50" height="50" class="mr-1">
</div>
<form method="POST" enctype="multipart/form-data">
<input type="file" accept="image/*" #change="onChange">
</form>
</div>
<script>
import axios from 'axios'
export default {
data() {
return {
setAuthHeader: axios.defaults.headers.common['Authorization'] = 'Bearer ' + this.$store.state.token,
};
},
computed: {
user() {
return this.$store.state.user
},
avatar() {
return this.user.avatar_path;
},
},
methods: {
}
}
So I have this code:
<template>
<div id="search-wrapper">
<div>
<input
id="search_input"
ref="autocomplete"
placeholder="Search"
class="search-location"
onfocus="value = ''"
#keyup.enter.native="displayPic"
>
</div>
</div>
</template>
<script>
import VueGoogleAutocomplete from "vue-google-autocomplete";
export default {
data: function() {
return {
search_input: {},
pic: {}
};
},
mounted() {
this.autocomplete = new google.maps.places.Autocomplete(
this.$refs.autocomplete
);
this.autocomplete.addListener("place_changed", () => {
let place = this.autocomplete.getPlace();
if (place.photos) {
place.photos.forEach(photo => {
let pic=place.photos[0].getUrl()
console.log(pic);
});
}
});
},
methods: {
displayPic(ref){
this.autocomplete = new google.maps.places.Autocomplete(
this.$refs.autocomplete);
this.autocomplete.addListener("place_changed", () => {
let place = this.autocomplete.getPlace();
if (place.photos) {
place.photos.forEach(photo => {
let pic=place.photos[0].getUrl()
console.log(pic);
});
}
})
},
}
}
I want to pass the "pic" parameter, resulted in displayPic, which is a function, into my template, after one of the locations is being selected.
I've tried several approaches, but I'm very new to Vue so it's a little bit tricky, at least until I'll understand how the components go.
Right now, the event is on enter, but I would like it to be triggered when a place is selected.
Any ideas how can I do that?
Right now, the most important thing is getting the pic value into my template.
<template>
<div id="search-wrapper">
<div>
<input style="width:500px;"
id="search_input"
ref="autocomplete"
placeholder="Search"
class="search-location"
onfocus="value = ''"
v-on:keyup.enter="displayPic"
#onclick="displayPic"
>
<img style="width:500px;;margin:5%;" :src="pic" >
</div>
</div>
</template>
<script>
import VueGoogleAutocomplete from "vue-google-autocomplete";
export default {
data: function() {
return {
search_input: {},
pic:""
};
},
mounted() {
this.autocomplete = new google.maps.places.Autocomplete(
this.$refs.autocomplete,
{componentRestrictions: {country: "us"}}
);
},
methods: {
displayPic: function(){
this.autocomplete.addListener("place_changed", () => {
let place = this.autocomplete.getPlace();
if (place.photos) {
place.photos.forEach(photo => {
this.pic=place.photos[0].getUrl()
});
}
})
},
}
}
</script>
I am trying to fetch results from database in News.vue, and display them in Topnews.vue. I have two links fetched. When I click link1, it shows up the Topnews.vue template with everything working as intended, however, if i click link2, nothing happens, except for that the URL changes, but the template does not show up the result. If i refresh the page and click link2 or click on the navbar, then link2, it shows up, and same, clicking then link1, changes the URL, but doesnt show up. I'm really stuck on that and I'd be really glad if you help me out on that issue. Hope you understand.
News.vue
<template id="news">
<div class="col-sm-5">
<div class="cars" v-for="row in filteredNews" >
<div class="name" >
<p class="desc_top_time">{{row.created_at}}</p>
<span class="last_p"> {{row.category}}</span>
<h3 style="margin-bottom:-4px; font-size: 16px;">
<router-link class="btn btn-primary" v-bind:to="{name: 'Topnews', params: {id: row.id} }">{{row.title}}</router-link></h3>
</div></div></div>
</template>
<script>
export default {
data: function() {
return {
news: [],
}
},
created: function() {
let uri = '/news';
Axios.get(uri).then((response) => {
this.news = response.data;
});
},
computed: {
filteredNews: function() {
if (this.news.length) {
return this.news;
}
}
}
}
</script>
Topnews.vue
<template id="topnews1">
<div class="col-sm-7">
<div class="cars">
<img :src="topnews.thumb" class="img-responsive" width=100%/>
<div class="name" ><h3>{{ topnews.title }}</h3>
<p>
<br>{{ topnews.info }}<br/>
</p>
</div></div></div>
</template>
<script>
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
created:function() {
let uri = '/news/'+this.$route.params.id;
Axios.get(uri).then((response) => {
this.topnews = response.data;
});
}
}
</script>
Like GoogleMac said Vue will reuse the same component whenever possible. Since the route for both IDs use the same component Vue will not recreate it, so the created() method is only being called on the first page. You'll need to use the routers beforeRouteUpdate to capture the route change and update the data.
in TopNews.vue:
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
beforeRouteEnter:function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
next(vm => {
vm.setData(response.data)
})
});
},
beforeRouteUpdate: function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
this.setData(response.data);
next();
});
},
methods: {
setData(data) {
this.topnews = data
}
}
}
If you click a link referring to the page you are on, nothing will change. Vue Router is smart enough to not make any changes.
My guess is that the IDs are messed up. If you are using Vue devtools you will be able to easily see what data is in each link. Are they as you expect.
I'm using Vuejs to display data from an API to a template. I'm trying to figure out why I am not returning data for the team so I can display in for the template. Right now when I use the VueJS Chrome Extention it shows that the team is an empty object.
<div v-if="team">
<div class="row">
<div class="col-12 col-sm-12">
<div class="fw-700 pb3 mb5" style="border-bottom: 1px solid #333;">Name:</div>
<div class="mb10" style="font-size: 20px;">{{ team.name }}</div>
</div>
</div>
<script>
module.exports = {
http: {
headers: {
'X-CSRF-TOKEN': window.Laravel.csrfToken
}
},
props: [ 'id', 'editable' ],
data: function(){
return {
modalName: 'additionalInfo',
team:{
}
}
};
},
methods: {
fetchInfo: function(){
this.$http.get('/api/teams/info', { params: { id: this.id } }).then((response) => {
this.team = response.data;
});
},
},
}
}
</script>
It is empty because your method fetchInfo isn't being called, so you need to do something like this:
created () {
this.fetchInfo()
}
This will call the function fetchInfo which in turn will fetch and fill this.team
I am using Vue component for my checkout form.
The stripe js (v3) file was included in the header section.
The form was in Component
This component has two section. One is to get payment details from the user and another is to submit card details.
<template>
<div class="payment_form">
<div id="payment_details" v-if="showPaymentDetails">
<!-- User input goes here. Like username phone email -->
</div>
<div id="stripe-form" v-if="showStripeForm">
<form action="/charge" method="post" id="payment-form" #submit.prevent="createStripeToken()">
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- a Stripe Element will be inserted here. -->
</div>
<!-- Used to display Element errors -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
</div>
</div>
</template>
<script>
import { Validator } from 'vee-validate';
export default {
data() {
return {
stripeToken: '',
showPaymentDetails: true,
showStripeForm: true,
}
},
created() {
},
methods: {
validateForm() {
self = this;
this.$validator.validateAll().then(result => {
if (result) {
// eslint-disable-next-line
alert('From Submitted!');
console.log(this.$data);
axios.post('/data',{
name:this.name,
})
.then(function (response) {
self.showStripeForm = true;
console.log(response);
})
.catch(function (error) {
console.log(error);
});
return;
}
});
},
createStripeToken(){
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
window.stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server
console.log(result.token);
}
});
});
},
initStripe(){
window.stripe = Stripe('stripe_test_key_here');
var elements = stripe.elements();
var style = {
base: {
// Add your base input styles here. For example:
fontSize: '16px',
lineHeight: '24px'
}
};
// Create an instance of the card Element
window.card = elements.create('card', {style: style});
// Add an instance of the card Element into the `card-element` <div>
window.card.mount('#card-element');
}
},
mounted() {
this.initStripe();
setTimeout(function () {
this.showStripeForm = false;
},2000);
}
}
</script>
I try to load the stripe form on page load and try to disable the element via showStripeForm.
But vue unset the loaded stripe card form from the stripe server and saved the dom to its original state.
So i can't trigger the stripe form on the axios callback.
I don't want to user stripe checkout and stripe js v1(getting input on your own form is deprecated after this version).
In mounted. Change the setTimeout callback to an arrow function, otherwise, this will point to Window instead of Vue.
mounted() {
setTimeout(() => {
this.showStripeForm = false
}, 2000)
}
Also, the way you access the DOM is not so Vue-ish. You could use ref on the DOM element you want to use in your code. For example:
<form action="/charge" method="post" ref="payment-form" #submit.prevent="createStripeToken()">
Then access it from $refs like this:
var form = this.$refs['payment-form']
/*
Same result as document.getElementById('payment-form')
but without using an id attribute.
*/