In my project I use medium-zoom with pagination. On first page it works well but on second, third, fourth... I have to click several times for close image...on second page...two times, on third 3 times...
it seems to be a problem with maybe any index number?
Here is my code:
<template>
<div>
<div v-if="loading" class="text-center">
<i class="fas fa-spinner fa-pulse fa-5x"></i>
</div>
<div v-else>
<div v-for="(item, imageIndex) in pageOfItems" :key="item.id" class="m-3">
<div class="row mt-3">
<div class="col-lg-9 my-auto" v-html="item.mytext"></div>
<div class="col-lg-3 my-auto text-center">
<article class="container">
<img
class="img-thumbnail"
:src="'http://localhost:4000/api/galeria/' + item.galeriaId + '_f.jpg'"
/>
</article>
</div>
</div>
<hr class="hr1" />
</div>
</div>
<div class="pb-0 pt-3 text-center">
<jw-pagination :items="info" :page-size="10" #changePage="onChangePage"></jw-pagination>
</div>
</div>
</template>
<script>
import mediumZoom from 'medium-zoom'
import axios from 'axios'
export default {
data() {
return {
info: [],
customLabels,
pageOfItems: [],
loading: true,
}
},
mounted() {
axios
.get('http://localhost:4000/api/fetch_galeria.php/')
.then((response) => (this.info = response.data))
.finally(() => (this.loading = false))
},
updated() {
mediumZoom('article img', {
background: 'transparent',
})
},
methods: {
onChangePage(pageOfItems) {
// update page of items
this.pageOfItems = pageOfItems
},
},
}
</script>
You will pretty much need to detach the event listeners here. Because it should be a lot of them added everytime due to the updated hook.
A naive implementation would be to add mediumZoom on the mounted hook. And when you do have a new changePage event, to detach it from all the images, then to apply it to the new ones with the same mediumZoom call.
Below is a way to see which event listeners (and probably how many) you have linked to a specific VueJS component. Select it in the Vue devtools and then, you will have access to the element's properties via $vm0.
Related
I'm working on this app and the idea is to show details of the cars in a sidebar on click. There are several issues like the sidebar is showing four times and I resolve it somehow but I don't know why is it showing four times. now I don't getting any response on emit call help me out please, I try $parent.$emit, $root.$emit but not seems working!!!
<template>
<div class="home">
<!-- warehouse details -->
<div
v-for="(detail, detailindex) in details"
:key="detailindex"
class="container mt-5 mb-5"
>
<h1>
{{ detail.name }}
<span class="location">{{ detail.cars.location }}</span>
</h1>
<!-- vehicle details -->
<SingleGarage :detail="detail"> </SingleGarage>
</div>
<b-sidebar
id="my-sidebar"
title="Sidebar with backdrop"
backdrop-variant="dark"
ref="mySidebar"
backdrop
shadow
#emitData="testingEmit()"
>
<div class="px-3 py-2">
<h1>{{currentCar}}</h1>
</div>
</b-sidebar>
</div>
</template>
<script>
// # is an alias to /src
import axios from "axios";
import SingleGarage from "../components/SingleGarage";
export default {
components: { SingleGarage },
name: "Home",
data: () => ({
details: String,
currentCar: 'String',
}),
methods:{
testingEmit(data){
this.currentCar = data
console.log('data from emit',data)
}
},
mounted() {
axios
.get("https://api.jsonbin.io/b/5ebe673947a2266b1478d892")
.then((response) => {
var results;
response.data.forEach((element) => {
element.cars.vehicles.sort((a, b) => {
a = new Date(a.date_added);
b = new Date(b.date_added);
results = a > b ? -1 : a < b ? 1 : 0;
return results * -1;
});
});
this.details = response.data;
});
},
};
</script>
<template>
<div class="vGrid mt-4">
<div
class="gridItem border vehicle singleCar"
v-for="(vehicle, vehicleIndex) in detail.cars.vehicles"
:class="'griditem' + vehicleIndex"
:key="vehicle._id"
>
<SingleCar
:vehicle="vehicle"
#click.native="testingTef(vehicleIndex)"
></SingleCar>
</div>
</div>
</template>
<script>
import SingleCar from "#/components/SingleCar";
export default {
name: "SingleGarage",
components: { SingleCar },
props: ["detail"],
data: () => ({
dummyImg: require("#/assets/img/dummycar.png"),
currentCar : 1
}),
methods: {
testingTef(vehicleIndex) {
this.$parent.$emit('emitData',this.detail.cars.vehicles[vehicleIndex].make)
this.$root.$emit('bv::toggle::collapse', 'my-sidebar')
console.log(this.detail.cars.vehicles[vehicleIndex].make)
console.log(this.detail.cars.vehicles[vehicleIndex].date_added)
this.currentCar = this.detail.cars.vehicles[vehicleIndex].make;
},
},
};
</script>
<template>
<div class="singleCar">
<!-- conditionally show image -->
<img
class="carImg"
:src="vehicle.img"
v-if="vehicle.img"
alt="No Preview"
/>
<img class="carImg" :src="dummyImg" v-else alt="No Preview" />
<div class="p-3">
<h3 class="make">{{ vehicle.make }}</h3>
<div class="modelDetails">
<div class="model d-flex ">
<p class="bold">Model:</p>
<p class="price ml-auto ">{{ vehicle.model }}</p>
</div>
<div class="price d-flex ">
<p class="bold">Price:</p>
<p class="price ml-auto ">€{{ vehicle.price }}</p>
</div>
</div>
<p class="dateAdded ml-auto ">{{ vehicle.date_added }}</p>
</div>
</div>
</template>
<script>
export default {
name: "SingleCar",
props: ["vehicle"],
data: () => ({
dummyImg: require("#/assets/img/dummycar.png"),
}),
methods:{
working(){
console.log('working');
console.log(this.vehicle.make)
}
}
};
</script>
Thanks for your help.
So a few things you can try to fix this
in your Home.vue you can change
#emitData="testingEmit()"
to
#emitData="testingEmit"
// or
#emitData="testingEmit($event)"
You are telling to the function testingEmit that is not params to parse. So you need to take out the () and Vue will parse everything that comes from the $event or you cant say put the $event as a param in your testingEmit (second option).
For your SingleGarage.vue you can take the $parent.$emit and replace it with
this.$emit('emitData',this.detail.cars.vehicles[vehicleIndex].make)
I am new to vue.js I am going to make a news app using a API.
In here I used v-for to iterate some codes. I think there is a problem in v-for. because it gives error. error has been included end of this question.
I used following codes to show search results
<template>
<div class="col-md-12">
<div>
<h2>Results</h2>
<div class="card mb-3" v-for="item in resultList">
<img class="card-img-top" src="" alt="Card image cap" height="100" width="200">
<div class="card-body">
<h5 class="card-title">{{ item.name }}</h5>
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="button">
<button type="button" class="btn btn-primary">Show more</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
props: ['resultList']
}
</script>
following codes used to get the api data(search api data)
<template>
<div class="container1">
<div class="form-group row">
<label for="exampleInputPassword1">Search Music</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="exampleInputPassword1" placeholder="Type here"
#input="keypressed">
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default{
data () {
return {
newset: []
}
},
methods: {
keypressed () {
var key = event.target.value
axios.get('http://newsapi.org/v2/everything?q=' + key +
'&sortBy=publishedAt&apiKey=b5ac77726d0a4460bd82b57210b2efb7')
.then(response => {
this.newset = response.data.articles
})
.catch(e => {
this.error.push(e)
})
this.$emit('newDataset', this.newset)
}
}
}
</script>
but it gives an error. error is
https://google.com/#q=vue%2Frequire-v-for-key Elements in iteration expect to have 'v-
bind:key'
directives
src\components\ResultArea.vue:5:5
<div class="card mb-3" v-for="item in resultList">
^
You need to add :key binding:
<div class="card mb-3" v-for="(item, index) in resultList" :key="index">
key value should be unique for every item. If your list items has any id it is good idea to use this id here:
<div class="card mb-3" v-for="item in resultList" :key="item.id">
To give Vue a hint so that it can track each node’s identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item. check the link for better understanding
https://v2.vuejs.org/v2/guide/list.html#Maintaining-State
I have this problem:
I render a list obtained by an API call with a v-for, and if you write into a form, only the elements that match the key written into the form are showed
Now, I need to sort this elements by name and by price too using a dropdown with buttons
is it possible?
Sorry for the external link, but I have some trouble pasting code into StackOverflow, maybe due the vue-boostrap
HTML part
code part
<div>
<b-dropdown id="dropdown-1" text="Dropdown Button" class="m-md-2">
<b-dropdown-item>Default Sort</b-dropdown-item>
<b-dropdown-divider></b-dropdown-divider>
<b-dropdown-item #click="sortByName">Sort by Name</b-dropdown-item>
<b-dropdown-divider></b-dropdown-divider>
<b-dropdown-item>Sot by Price</b-dropdown-item>
</b-dropdown>
</div>
<div class="d-flex flex-wrap justify-content-center">
<div class="card" v-for="product in filteredCatalogue" :key="product.id">
<img class="product pimage" :src="product.images.small" />
<hr class="product black-line" />
<h5 class="product name text-uppercase">{{product.name}}</h5>
<h5 class="product short-description">{{product.descriptions.short}}</h5>
<h5
class="product price"
v-if="product.price.currency_symbol=='€'"
>€ {{product.price.sell}}</h5>
<b-button id="button-shop" squared variant="warning">
<i class="fas fa-shopping-cart"></i>
<div id="yellow-button-text">ADD TO CART</div>
</b-button>
</div>
</div>
<form class="form-inline justify-content-center">
<div class="form-group">
<input
class="form-control bg-white border border-secondary"
type="text"
v-model="key"
placeholder="Cerca tra i prodotti"
value
autocomplete="off"
/>
</div>
</form>
import axios from "axios";
import { cacheAdapterEnhancer } from "axios-extensions";
export default {
data() {
return {
catalogue: [],
key: ""
};
},
created() {
axios
.get(
API_URL,
cacheAdapterEnhancer
)
.then(response => {
this.catalogue = response.data;
console.log(this.catalogue);
})
.catch(error => console.log(error));
},
computed: {
filteredCatalogue: function() {
return this.catalogue.filter(product => {
return product.name.toLowerCase().match(this.key.toLowerCase());
});
}
}
};
Check sort method on JavaScrtipt array here.
when I make a call to the unsplash api in the componentDidMount, the this.state.photos data is still undefined after the api call, but when I console.log out the response to the console, the data is showing. You can see thay I have console.log the informations. (response.data and this.state.photos). The this.state.photos is the one being undefined and not showing the data. I am using ReactJS. Anyone know why? Please see code below. Thanks!
import React, { useState, useEffect } from "react";
import Footer from "../components/Footer";
import axios from "axios";
import NavBar from "../components/NavBar";
import AOS from 'aos';
class CountryPage extends React.Component{
state = {
country: null,
photos: []
}
componentDidMount() {
axios.get(`https://restcountries.eu/rest/v2/name/${this.props.match.params.name}`)
.then(response=> {
this.setState({
country: response.data[0]
})
console.log(this.state.country)
})
axios.get(`https://api.unsplash.com/search/photos?client_id=${Access_Key}&query=bike`)
.then(response=> {
this.setState({
photos: response.data[0]
})
console.log(response.data)
console.log(this.state.photos)
})
AOS.init({
duration: 1000
});
}
render() {
if(!this.state.country){
return <p>Loading</p>
}
return(
<div className="countryPageBack">
<NavBar/>
<div className="container">
<div className="countryAndFlag animate__bounceIn">
<h1 className="countryPageTitle">{`${this.props.match.params.name} - (${this.state.country.nativeName})`}</h1>
<img className="countryPageFlag" src={this.state.country.flag}/>
</div>
<div data-aos="fade-left" className="countryBasics">
<h3 className="sectionTitle">Basic Info</h3>
<div className="basicDesc">{`Located in ${this.state.country.region}, ${this.state.country.name} has a population of ${this.state.country.population} people`}</div>
<div className="propertyName">Currency: <span className="propertyValue">{this.state.country.currencies[0].name} - {`${this.state.country.currencies[0].symbol}`}</span></div>
<div className="propertyName">Langauges Spoken: <span className="propertyValue">{this.state.country.languages[0].name}</span></div>
<div className="propertyName">Capitols: <span className="propertyValue">{this.state.country.capital}</span></div>
</div>
<div data-aos="fade-right" className="countryRegion">
<h3 className="sectionTitle">Regional Info</h3>
<div className="propertyName">Region: <span className="propertyValue">{this.state.country.region}</span></div>
<div className="propertyName">Subregion: <span className="propertyValue">{this.state.country.subregion}</span></div>
<div className="propertyName">Timezones: <span className="propertyValue">{this.state.country.timezones}</span></div>
<div className="propertyName">Area: <span className="propertyValue">{this.state.country.area}</span></div>
</div>
<div data-aos="fade-left" className="countryOthers">
<h3 className="sectionTitle">Other Info</h3>
<div className="propertyName">Calling Code: <span className="propertyValue">{this.state.country.callingCodes}</span></div>
<div className="propertyName">Country Code: <span className="propertyValue">{this.state.country.alpha3Code}</span></div>
<div className="propertyName">Main Domain: <span className="propertyValue">{this.state.country.topLevelDomain}</span></div>
</div>
</div>
<Footer/>
</div>
)
}
}
export default CountryPage;
Looks like you are setting the wrong key to the state.
I tried a similar call to Unsplash API (ref) for fetching photos. I believe you should change your set code to
this.setState({
photos: response.data.results[0]
})
in case you want the first photo to be added to photos or response.data.results if you want the entire list.
I'm trying to use a combination of v-for and v-model to get a two-way data bind for some input forms. I want to dynamically create child components. Currently, I don't see the child component update the parent's data object.
My template looks like this
<div class="container" id="app">
<div class="row">
Parent Val
{{ ranges }}
</div>
<div class="row">
<button
v-on:click="addRange"
type="button"
class="btn btn-outline-secondary">Add time-range
</button>
</div>
<time-range
v-for="range in ranges"
:box-index="$index"
v-bind:data.sync="range">
</time-range>
</div>
<template id="time-range">
<div class="row">
<input v-model="data" type="text">
</div>
</template>
and the js this
Vue.component('time-range', {
template: '#time-range',
props: ['data'],
data: {}
})
new Vue({
el: '#app',
data: {
ranges: [],
},
methods: {
addRange: function () {
this.ranges.push('')
},
}
})
I've also made a js fiddle as well https://jsfiddle.net/8mdso9fj/96/
Note: the use of an array complicates things: you cannot modify an alias (the v-for variable).
One approach that isn't mentioned often is to catch the native input event as it bubbles up to the component. This can be a little simpler than having to propagate Vue events up the chain, as long as you know there's an element issuing a native input or change event somewhere in your component. I'm using change for this example, so you won't see it happen until you leave the field. Due to the array issue, I have to use splice to have Vue notice the change to an element.
Vue.component('time-range', {
template: '#time-range',
props: ['data']
})
new Vue({
el: '#app',
data: {
ranges: [],
},
methods: {
addRange: function () {
this.ranges.push('')
},
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div class="container" id="app">
<div class="row">
Parent Val
{{ ranges }}
</div>
<div class="row">
<button
v-on:click="addRange"
type="button"
class="btn btn-outline-secondary">Add time-range
</button>
</div>
<time-range
v-for="range, index in ranges"
:data="range"
:key="index"
#change.native="(event) => ranges.splice(index, 1, event.target.value)">
</time-range>
</div>
<template id="time-range">
<div class="row">
<input :value="data" type="text">
</div>
</template>
To use the .sync modifier, the child component has to emit an update:variablename event that the parent will catch and do its magic. In this case, variablename is data. You still have to use the array-subscripting notation, because you still can't modify a v-for alias variable, but Vue is smart about .sync on the array element, so there's no messy splice.
Vue.component('time-range', {
template: '#time-range',
props: ['data'],
methods: {
emitUpdate(event) {
this.$emit('update:data', event.target.value);
}
}
})
new Vue({
el: '#app',
data: {
ranges: [],
},
methods: {
addRange: function () {
this.ranges.push('')
},
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div class="container" id="app">
<div class="row">
Parent Val
{{ ranges }}
</div>
<div class="row">
<button
v-on:click="addRange"
type="button"
class="btn btn-outline-secondary">Add time-range
</button>
</div>
<time-range
v-for="range, index in ranges"
:data.sync="ranges[index]"
:key="index">
</time-range>
</div>
<template id="time-range">
<div class="row">
<input :value="data" type="text" #change="emitUpdate">
</div>
</template>