I'm loading a set of items from a json service calling using vuex in vuejs. Items will be taken to a array and render in a different component. I tried using transition-group to add a transition seperately for a item. But it did not work. So i'm thinking of rendering item and fadeIn item by item and need to make a delay between one item and other item.
My code is below
ProductItem.vue
<template>
<transition name="slide-fade">
<div class="product-container">
<div class="product-box">
<div class="product-image">
<img v-bind:src="product.values['3'].value" alt>
</div>
<div class="product-details">
<div class="product-name">{{product.values[0].value}}</div>
<div class="product-description" v-if="product" v-html="productDescription"></div>
<div class="product-price">From 295 LKR</div>
<div class="product-action">
<button class="btn-choose" #click="closeBox">
<i class="fas fa-check fa-icon"></i> Choose
</button>
</div>
</div>
</div>
</div>
</transition>
</template>
Products.vue
<template>
<div>
<div class="product-list-container clearfix">
<div v-if="!hasProducts">
<product-item-loader v-for="itemLoader in 8" v-bind:key="itemLoader"></product-item-loader>
</div>
<transition name="fade">
<div v-if="hasProducts">
<product-item v-for="pItem in productsList" :product="pItem" v-bind:key="pItem"></product-item>
</div>
</transition>
</div>
</div>
</template>
<script>
import productItem from "./ProductItem.vue";
import productItemLoader from "./ProductItemLoader.vue";
export default {
components: {
productItem,
productItemLoader
},
data() {
return {
iconCalendar:
"M39.58,115.5h70.84a7.11,7.11,0,0,0,7.08-7.08V48.21a7.11,7.11,0,0,0-7.08-7.09H99.79V34a3.54,3.54,0,0,0-7.08,0v7.08H57.29V34a3.54,3.54,0,1,0-7.08,0v7.08H39.58a7.11,7.11,0,0,0-7.08,7.09v60.21A7.11,7.11,0,0,0,39.58,115.5Zm0-67.29H50.21v3.54a3.54,3.54,0,0,0,7.08,0V48.21H92.71v3.54a3.54,3.54,0,0,0,7.08,0V48.21h10.63V62.38H39.58Zm0,21.25h70.84v39H39.58Z",
productsList: [],
hasProducts: false,
date: "2012-12-01"
};
},
methods: {
optionChanged: function(selection) {
this.getProducts(selection.name);
},
getProducts: function(date) {
self = this;
self.hasProducts = false;
this.restAPI
.get("", {
params: {
after: date,
until: date,
language: "en"
}
})
.then(function(response) {
self.productsList = response.data.content.classes[0].objects;
self.productsList.length > 0
? (self.hasProducts = true)
: (self.hasProducts = false);
})
.catch(e => {
console.log(e);
});
}
},
beforeMount() {
self.hasProducts = false;
this.getProducts();
}
};
</script>
You can achieve a nice staggered effect by using setInterval:
new Vue({
el: '#app',
data() {
return {
loading: false,
items: [],
remaining: 3000
}
},
mounted() {
this.loading = true
const timer = () => {
return setInterval(() => {
this.remaining -= 1000
}, 1000)
}
// simulate api call
const ti = timer()
setTimeout(() => {
const items = Array.from(Array(9), (x, i) => {
return {
name: `Product ${i + 1}`,
description: `Product ${i + 1} description`
}
})
clearInterval(ti)
this.loading = false
const interval = setInterval(() => {
if (!items.length) {
clearInterval(interval)
} else {
this.items.push(items.shift())
}
}, 360)
}, 3000)
},
methods: {
renderNext(item) {
this.items.push(item)
}
}
})
li {
width: 30%;
padding: .3rem;
}
li.striped {
background-color: rgba(128,203,196,.4);
}
.list-complete-item {
transition: all 1s;
display: inline-block;
margin-right: 0px;
}
.list-complete-enter,
.list-complete-leave-to
/* .list-complete-leave-active below version 2.1.8 */
{
opacity: 0;
transform: translateY(30px);
}
.list-complete-leave-active {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h1 v-if="loading">Loading... {{ remaining / 1000 }}</h1>
<transition-group name="list-complete" tag="ul">
<li v-for="(item, i) in items" v-bind:key="item.name" :class="`list-complete-item ${i % 2 === 0 ? 'striped' : ''}`">
<span>{{ item.name }}</span><br/>
<span class="description">{{ item.description }}</span>
</li>
</transition-group>
</div>
Related
I have a + button to add item to cart,it works. But at the same time when number increments in cart I need number that says in stock will decrement number in stock that displays it doesn't work …
I need to do the same with - button
I really need the displayed inStock number to change when I add or remove items from cart
Can someone please help me? I'm new at this
Vue.component('product', {
template:
`
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ product }}</h1>
<p v-if="inStock">In Stock {{inStock}}</p>
<p v-else>Out of Stock</p>
<button #click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"> + </button>
<button #click="removeFromCart"> - </button>
</div>
</div>
`,
data() {
return {
product: 'Hoodie',
image: 'hoodie.jpeg',
inStock: 10,
cart: 0
}
},
methods: {
addToCart: function() {
this.$emit('add-to-cart')
},
removeFromCart: function() {
this.$emit('remove-from-cart')
}
}
})
var app = new Vue({
el: '#app',
data: {
cart: 0,
inStock: 10
},
methods: {
updateCart() {
this.cart += 1,
this.inStock -= 1,
},
removeItem() {
this.cart -= 1,
this.inStock += 1
}
}
})
HTML:
<div class="nav-bar"></div>
<div id="app">
<div class="cart">
<p>Cart({{ cart }})</p>
</div>
<product #add-to-cart="updateCart" #remove-from-cart="removeItem"></product>
</div>
Inside your component, remove the data attributes inStock and cart and props section.
Vue.component('product', {
template:
`
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ product }}</h1>
<p v-if="inStock">In Stock {{inStock}}</p>
<p v-else>Out of Stock</p>
<button #click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"> + </button>
<button #click="removeFromCart"> - </button>
</div>
</div>
`,
data() {
return {
product: 'Hoodie',
image: 'hoodie.jpeg',
}
},
props: {
inStock: {
type: Number,
default: 0
},
cart : {
type: Number,
default: 0
}
},
methods: {
addToCart: function() {
this.$emit('add-to-cart')
},
removeFromCart: function() {
this.$emit('remove-from-cart')
}
}
})
var app = new Vue({
el: '#app',
data: {
cart: 0,
inStock: 10
},
methods: {
updateCart() {
this.cart += 1,
this.inStock -= 1,
},
removeItem() {
this.cart -= 1,
this.inStock += 1
}
}
})
Then in your parent component, pass the props.
Parent Component (HTML)
<div class="nav-bar"></div>
<div id="app">
<div class="cart">
<p>Cart({{ cart }})</p>
</div>
<!-- changes for props added below -->
<product :cart="cart" :inStock="inStock" #add-to-cart="updateCart" #remove-from-cart="removeItem"></product>
</div>
I have four pictures, when I hover the mouse over them, a certain component is displayed from below, but I still need to bind the click event, that is, when I click on the picture, the component should be displayed; when the component is clicked again, the problem is that I cannot bind two events at once at the same time
I understand that when the user hovers over the component, it will be immediately displayed and the click event will be useless, but I will need it in the mobile version
You can also look at my code in codesandbox
<template>
<div>
<div class="enjoy_headline_container">
<div class="EnjoyGirlsContainer">
<div>
<h3 style="margin: 0"></h3>
</div>
<div class="EnjoyGirlsList">
<div
v-for="(chunk, index) in Math.ceil(EnjoyGirlsList.length / 2)"
:key="'chunk-' + index"
:class="'wrap-' + index"
>
<div
v-for="(item, index) in EnjoyGirlsList.slice(
(chunk - 1) * 2,
chunk * 2
)"
:key="'img-' + index"
class="EnjoyCard"
:class="'EnjoyCard-' + index"
>
<div v-on:click="isHidden = !isHidden">
<img
#mouseover="mouseOver(item, (hover = true))"
:src="item.imagePath"
alt="Snow"
/>
</div>
<div class="EnjoyCardContainer">
<div
:style="{ background: item.textColor }"
class="EnjoyCardChildContainer"
>
<h3 class="EnjoyCardChildContainerTitleName">
{{ item.titleName }}
</h3>
</div>
</div>
<div
v-if="selected === item && !isHidden"
class="below-all-description EnjoyGirlsHoverEffect"
>
<div class="next-to-description EnjoyGirlsHoverEffect">
<div
style="width: 100%; display: flex; justify-content: center"
v-for="(enjoy, index) in EnjoyGirlsList"
:key="index"
>
<div
#mouseleave="mouseout(enjoy, (hover = false))"
class="EnjoyGirlsChildHoverEffect"
>
<component
v-show="enjoy.hovered"
v-bind:is="enjoy.componentName"
></component>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-if="!isHidden" class="below-all-description">
<template v-if="selected === null"></template>
<template v-else>
<div
style="width: 100%; display: flex; justify-content: center"
v-for="(enjoy, index) in EnjoyGirlsList"
:key="index"
>
<div
#mouseleave="mouseout(enjoy, (hover = false))"
class="EnjoyGirlsChildHoverEffect"
>
<component
v-show="enjoy.hovered"
v-bind:is="enjoy.componentName"
></component>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script>
import EnjoyBlue from "./components/EnjoyBlue";
import EnjoyGreen from "./components/EnjoyGreen";
import EnjoyYellow from "./components/EnjoyYellow";
import EnjoyRed from "./components/EnjoyRed";
export default {
name: "HomePage",
components: {
EnjoyRed,
EnjoyYellow,
EnjoyGreen,
EnjoyBlue,
},
data() {
return {
isHidden: false,
selected: null,
hover: false,
sectionGirlsListComponentsNames: [
"EnjoyRed",
"EnjoyYellow",
"EnjoyGreen",
"EnjoyBlue",
],
EnjoyGirlsList: [
{
imagePath: "https://i.ibb.co/mCpNXhG/IMG-6061-min.png",
titleName: "TEENS",
textColor: "#74C8C5",
hovered: false,
componentName: "EnjoyBlue",
},
{
imagePath: "https://i.ibb.co/WvJjwsN/Rectangle-2.png",
titleName: "MINXES",
textColor: "#76ED00",
hovered: false,
componentName: "EnjoyGreen",
},
{
imagePath: "https://i.ibb.co/7khc5f0/Rectangle-3.png",
titleName: "MILFS",
textColor: "#FFE600",
hovered: false,
componentName: "EnjoyYellow",
},
{
imagePath: "https://i.ibb.co/6nz97Bw/Rectangle-4.png",
titleName: "COURGARS",
textColor: "#CC003D",
hovered: false,
componentName: "EnjoyRed",
},
],
};
},
methods: {
mouseOver: function (enjoy) {
this.EnjoyGirlsList.forEach((enjoy) => (enjoy.hovered = false));
this.selected = enjoy;
enjoy.hovered = true;
if (this.hover) {
console.log("4949494");
}
},
mouseout: function (enjoy) {
this.selected = null;
enjoy.hovered = false;
},
mouseEnter: function () {},
Prev() {
this.$refs.carousel.prev();
},
showNext() {
this.$refs.carousel.next();
},
},
};
</script>
And so if you looked at this code in codesandbox (I left the link at the top), then you might have noticed that hover only works the first time after that it does not work, only the click event works
Your click event toggles isHidden. When you click on it you set isHidden to true. After that it won't show, if you hover over it since you are hiding it with v-if:
<div v-if="!isHidden" class="below-all-description">
...
</div>
Solution:
In your mouseOver function you explicitly have to set isHidden to false.
methods: {
mouseOver: function (enjoy) {
this.isHidden = false;
this.EnjoyGirlsList.forEach((enjoy) => (enjoy.hovered = false));
...
}
...
}
This is in relation to implementing drag and drop using vue. I have a draggable div container.
// BoardColumn Component Parent Container
<template>
<div
draggable
#dragover.prevent
#dragenter.prevent
#drop="dropColumn($event, columnIndex)"
#dragstart="setPickColumnInfo($event, columnIndex)"
>
<div class="column bg-grey-light m-4">
<div class="flex items-center mb-2 font-bold">
{{ getColumnName() }}
</div>
<div class="list-reset">
<TaskCard
class="task"
v-for="(task, $taskIndex) of columnData.tasks"
:key="$taskIndex"
v-bind:task="task"
v-bind:taskIndex="$taskIndex"
v-bind:columnIndex="columnIndex"
/>
</div>
<input
type="text"
class="block p-2 w-full bg-transparent"
placeholder="+Enter a new task"
#keyup.enter="createTask($event, columnData.tasks)"
/>
</div>
</div>
</template>
Inside this container is another draggable container TaskCard. i'm able to drag the parent container and the child container without any issues. However, the callback functions related to only parent container BoardColumn gets fired regardless of which container is being dragged.
The function definitions are below. Any help would be greatly appreciated.
// TaskCard.vue Child container
<template>
<div
draggable
#dragover.prevent
#dragenter.prevent
#dragstart="setPickupTaskInfo($event, taskIndex, columnIndex)"
#dragend="dropTask($event, taskIndex, columnIndex)"
#click="goToTask(task)"
v-if="isTaskOpen === false"
>
<span class="w-full flex-no-shrink font-bold">
{{ task.name }}
</span>
<p v-if="task.description" class="w-full flex-no-shrink mt-1 text-sm">
{{ task.description }}
</p>
</div>
<div class="task-bg" #click.self="close" v-else>
<router-view />
</div>
</template>
// BoardColumn JS
import TaskCard from "#/components/TaskCard/TaskCard.vue";
export default {
components: {
TaskCard
},
props: ["columnData", "columnIndex"],
methods: {
getColumnName() {
return this.columnData.name;
},
createTask(e, tasks) {
this.$store.commit("CREATE_TASK", {
tasks,
name: e.target.value
});
e.target.value = "";
},
setPickColumnInfo(e, fromColumnIndex) {
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.dropEffect = "move";
e.dataTransfer.setData("from-column-index", fromColumnIndex);
e.dataTransfer.setData("type", "column");
},
moveColumn(fromColumnIndex, toColumnIndex) {
this.$store.commit("MOVE_COLUMN", {
fromColumnIndex,
toColumnIndex
});
},
dropColumn(e, toColumnIndex) {
if (e.dataTransfer.getData("type") === "column") {
this.moveColumn(
e.dataTransfer.getData("from-column-index"),
toColumnIndex
);
console.log(e.dataTransfer.getData("type"));
}
}
}
};
// TaskCard JS
export default {
props: ["task", "taskIndex", "columnIndex"],
data() {
return {
isTaskOpen: false
};
},
methods: {
goToTask(task) {
this.$router.push({ name: "task", params: { id: task.id } });
this.isTaskOpen = true;
},
close() {
this.$router.push({ name: "board" });
this.isTaskOpen = false;
},
setPickupTaskInfo(e, fromTaskIndex, fromColumnIndex) {
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.dropEffect = "move";
e.dataTransfer.setData("from-task-index", fromTaskIndex);
e.dataTransfer.setData("from-column-index", fromColumnIndex);
e.dataTransfer.setData("type", "task");
console.log(e);
},
moveTask(e, toTaskIndex, toColumnIndex) {
const fromColumnIndex = e.dataTransfer.getData("from-column-index");
const fromTaskIndex = e.dataTransfer.getData("from-task-index");
this.$store.commit("MOVE_TASK", {
fromTaskIndex,
fromColumnIndex,
toTaskIndex,
toColumnIndex
});
},
dropTask(e, toTaskIndex, toColumnIndex) {
if (e.dataTransfer.getData("type") === "task") {
console.log(e.dataTransfer.getData("type"));
this.moveTask(e, toTaskIndex, toColumnIndex);
}
}
}
};
I am using the Itunes rest api to get data into my application, I am having problem getting the data into the table, the rest api structure is as follows:
{resultCount: 4, results: Array(4)}
So far I have tried the following:
<div class="overflow-auto">
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
aria-controls="my-table"
></b-pagination>
<p class="mt-3">Current Page: {{ currentPage }}</p>
<b-table
id="my-table"
v-for="(result, index) in result"
:key="index"
:fields="fields"
:per-page="perPage"
:current-page="currentPage"
small
></b-table>
</div>
<script>
import List from "../components/myList.vue";
export default {
name: "Hero",
components: {
List
},
data: function() {
return {
fields: [
{
key: "artistName",
label: "Artist"
},
{
key: "collectionName",
label: "Song title"
}
],
title: "Simple Search",
isActive: true,
intro: "This is a simple hero unit, a simple jumbotron-style.",
subintro:
"It uses utility classes for typography and spacing to space content out.",
result: [],
errors: [],
List: [],
search: "",
loading: "",
message: false,
isValidationAllowed: false,
loadingClass: "loading",
errorTextClass: "error-text",
disableButton: false,
perPage: 3,
currentPage: 1
};
},
watch: {
search: function(val) {
if (!val) {
this.result = [];
}
}
},
computed: {
validated() {
return this.isValidationAllowed && !this.search;
},
isDisabled: function() {
return !this.terms;
},
rows() {
return this.result.length;
}
},
methods: {
getData: function() {
this.isValidationAllowed = true;
this.loading = true;
fetch(`https://itunes.apple.com/search?term=${this.search}&entity=album`)
.then(response => response.json())
.then(data => {
this.result = data.results;
this.loading = false;
/* eslint-disable no-console */
console.log(data);
/* eslint-disable no-console */
});
},
toggleClass: function() {
// Check value
if (this.isActive) {
this.isActive = false;
} else {
this.isActive = true;
}
},
refreshPage: function() {
this.search = "";
},
addItem: function(result) {
result.disableButton = true; // Or result['disableButton'] = true;
this.List.push(result);
/* eslint-disable no-console */
console.log(result);
/* eslint-disable no-console */
},
resizeArtworkUrl(result) {
return result.artworkUrl100.replace("100x100", "160x160");
}
},
mounted() {
if (localStorage.getItem("List")) {
try {
this.List = JSON.parse(localStorage.getItem("List"));
} catch (err) {
console.err(err);
}
}
}
};
</script>
I get just [Object Object] when looking into the rendered page, so either I am not targeting the correct element, or it is not coming in right: the following code works outside of the bootstrap vue pagination and table.
<div v-for="(result, index) in result" :key="index">
<div class="media mb-4">
<img
:src="resizeArtworkUrl(result)"
alt="Album Cover"
class="album-cover align-self-start mr-3"
>
<div class="media-body">
<h4 class="mt-0">
<button
type="button"
class="btn btn-primary btn-lg mb-3 float-right"
v-on:click="addItem(result)"
:disabled="result.disableButton"
>
<font-awesome-icon icon="plus"/>
</button>
<b>{{result.collectionName}}</b>
</h4>
<h6 class="mt-0">{{result.artistName}}</h6>
<p class="mt-0">{{result.primaryGenreName}}</p>
</div>
</div>
</div>
Any help would be good.
In your template has two variables with same name:
<div v-for="(result, index) in result" :key="index">
try change the result name like this:
<div v-for="(item, index) in result" :key="index">
<div class="media mb-4">
<img
:src="resizeArtworkUrl(item)"
alt="Album Cover"
class="album-cover align-self-start mr-3"
>
<div class="media-body">
<h4 class="mt-0">
<button
type="button"
class="btn btn-primary btn-lg mb-3 float-right"
v-on:click="addItem(item)"
:disabled="item.disableButton"
>
<font-awesome-icon icon="plus"/>
</button>
<b>{{item.collectionName}}</b>
</h4>
<h6 class="mt-0">{{item.artistName}}</h6>
<p class="mt-0">{{item.primaryGenreName}}</p>
</div>
</div>
</div>
I have read several examples with the same error but they do not fit my case very well and I can not understand why this error occurs. "Property or method" data "is not defined", if I do it in the first way I have no problem but if I do it as in the second one yes. why?
Thanks so much for any help¡
First Way (This works)
RenderD.vue
<script>
import { Doughnut, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
extends: Doughnut,
mixins: [reactiveProp],
props: ['options'],
mounted () {
this.renderChart(this.chartData, {tresponsive: true,
maintainAspectRatio: false})
}
}
</script>
ReactD.vue
<template>
<div class="AngleDisplay">
<div class="row ">
<div class="col-xl-12">
<h2>Angle-Display</h2>
</div>
</div>
<div class="row">
<RenderD :chart-data="datacollection" class="col-xl-12"></RenderD>
</div>
</div>
</template>
<script>
import RenderD from './RenderD'
export default {
components: {
RenderD
},
data () {
return {
datacollection: null
}
},
mounted () {
setInterval(() => {
this.fillData()
}, 100)
},
methods: {
fillData () {
this.datacollection = {
labels: ['Speed', 'Blank'],
datasets: [
{
label: ['Speed', 'Blank'],
backgroundColor: ['#A8201A', '#001427'],
borderColor: '#001427',
data: [this.getRandomInt(), this.getRandomInt()]
}
]
}
},
getRandomInt () {
return Math.floor(Math.random() * (50 - 5 + 1)) + 5
}
}
}
</script>
<style>
</style>
RadarMain.vue
<template>
<div id="RadarMain">
<div class="container-fluid">
<div class="row col-xl-12 title">
<h1 class="col-xl-12 text-center">Arduino-Radar Interface</h1>
</div>
<div class="row col-xl-12 ">
<React class="col-xl-8"></React>
<div class="col-xl-4 ">
<div class="row">
<ReactD :chart-data="data" class="col-xl-12"></ReactD>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import React from './components/React'
import ReactD from './components/ReactD'
export default {
name: 'RadarMain',
components: {
React,
ReactD
},
}
</script>
<style>
body {
background-color: #001427;
color: #FDFFFF;
}
h1 {
margin-top: 50px;
display: block;
}
#media screen and (max-width:1200px) {
.angle-display {
display: none;
}
}
#media screen and (max-width:550px) {
.title {
padding-bottom:50px;
}
}
</style>
Second Way(This don´t works)
ReactD.vue
<script>
import { Doughnut, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
name: 'ReactD',
extends: Doughnut,
mixins: [reactiveProp],
props: ['options'],
components: {
},
data () {
return {
datacollection: {}
}
},
methods: {
fillData () {
this.datacollection = {
labels: ['Speed', 'Blank'],
datasets: [
{
label: ['Speed', 'Blank'],
backgroundColor: ['#A8201A', '#001427'],
borderColor: '#001427',
data: [this.getRandomInt(), this.getRandomInt()]
}
]
}
},
getRandomInt () {
return Math.floor(Math.random() * (50 - 5 + 1)) + 5
}
},
mounted () {
this.renderChart(this.chartData, this.options)
setInterval(() => {
this.fillData()
}, 100)
}
}
</script>
RadarMain.vue
<template>
<div id="RadarMain">
<div class="container-fluid">
<div class="row col-xl-12 title">
<h1 class="col-xl-12 text-center">Arduino-Radar Interface</h1>
</div>
<div class="row col-xl-12 ">
<React class="col-xl-8"></React>
<div class="col-xl-4 ">
<div class="row">
<ReactD :chart-data="datacollection" class="col-xl-12"></ReactD>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import React from './components/React'
import ReactD from './components/ReactD'
export default {
name: 'RadarMain',
components: {
React,
ReactD
}
}
</script>
<style>
body {
background-color: #001427;
color: #FDFFFF;
}
h1 {
margin-top: 50px;
display: block;
}
#media screen and (max-width:1200px) {
.angle-display {
display: none;
}
}
#media screen and (max-width:550px) {
.title {
padding-bottom:50px;
}
}
</style>