How to implement your own action buttons using the vue-slick package - javascript

I am creating a slider using the vue-slick package, I am aware that this package allows you to style your buttons / arrows that it provides, but I wanted to create my own
own working buttons that changed the picture by clicking I don't know maybe this package provides such an opportunity here is the link of my project in codesandbox, Link to the documentation for this package
<template>
<div class="drag">
<VueSlickCarousel v-bind="settings">
<div v-for="(item, index) in homePageImageList" :key="index" class="hero-image"
:style="{ backgroundImage: 'url(' + item.imageURL + ')' }">
<div class="hero-text">
<div>
<button>Prev</button>
</div>
<div class="slide-counter">
<h4>{{ index + 1 }} / {{ homePageImageList.length }}</h4>
</div>
<div>
<button>Next</button>
</div>
</div>
</div>
</VueSlickCarousel>
</div>
</template>
<script>
import 'vue-slick-carousel/dist/vue-slick-carousel.css'
import 'vue-slick-carousel/dist/vue-slick-carousel-theme.css'
import VueSlickCarousel from 'vue-slick-carousel'
export default {
components: {VueSlickCarousel},
name: 'HelloWorld',
data() {
return {
homePageImageList: [
{
imageURL: "http://astragem.com/static/images/MenuGirl/HomePageBackground/15-min.png",
},
{
imageURL: "http://astragem.com/static/images/MenuGirl/HomePageBackground/15-min.png",
},
{
imageURL: "http://astragem.com/static/images/MenuGirl/HomePageBackground/15-min.png",
}
],
settings: {
"dots": false,
"dotsClass": "slick-dots custom-dot-class",
"edgeFriction": 0.35,
"infinite": false,
"speed": 500,
"slidesToShow": 1,
"slidesToScroll": 1,
"arrows": false,
}
}
}
}
</script>

<template>
<div class="drag">
<VueSlickCarousel v-bind="settings" ref="carousel">
<div v-for="(item, index) in homePageImageList" :key="index" class="hero-image"
:style="{ backgroundImage: 'url(' + item.imageURL + ')' }">
<div class="hero-text">
<div>
<button #click="Prev">Prev</button>
</div>
<div class="slide-counter">
<h4>{{ index + 1 }} / {{ homePageImageList.length }}</h4>
</div>
<div>
<button #click="showNext">show me the next</button>
</div>
</div>
</div>
</VueSlickCarousel>
</div>
</template>
<script>
import 'vue-slick-carousel/dist/vue-slick-carousel.css'
import 'vue-slick-carousel/dist/vue-slick-carousel-theme.css'
import VueSlickCarousel from 'vue-slick-carousel'
export default {
components: {VueSlickCarousel},
name: 'HelloWorld',
methods: {
Prev() {
this.$refs.carousel.prev()
},
showNext() {
this.$refs.carousel.next()
},
},
data() {
return {
homePageImageList: [
{
imageURL: "http://astragem.com/static/images/MenuGirl/HomePageBackground/15-min.png",
},
{
imageURL: "http://astragem.com/static/images/MenuGirl/HomePageBackground/15-min.png",
},
{
imageURL: "http://astragem.com/static/images/MenuGirl/HomePageBackground/15-min.png",
}
],
settings: {
"dots": false,
"dotsClass": "slick-dots custom-dot-class",
"edgeFriction": 0.35,
"infinite": false,
"speed": 500,
"slidesToShow": 1,
"slidesToScroll": 1,
"arrows": false,
},
}
}
}
</script>

Related

v-for adding two props in the same component vuejs

Guys
I want to do a v-for using a component that has two differents props
COMPONENT
<template>
<div class="bg-light rounded p-2 px-5">
<h5> {{ cardNumber }}</h5>
<h3>{{ cardItem }}</h3>
</div>
</template>
<script>
export default {
name: 'HighlightCard',
props: ['cardItem', 'cardNumber']
}
</script>
V-FOR INSIDE OTHER COMPONENT
<template>
<div class="row m-auto">
<HighlightCard
v-for="(itemCard, index) in cardItems"
:key="index"
:cardItem="itemCard"
class="col m-3"/>
</div>
</template>
<script>
import HighlightCard from './HighlightCard.vue';
export default {
name: 'TopDashboard',
components: {
HighlightCard
},
data () {
return {
cardItems: ['Impressões', 'Cliques', 'Conversões', 'Custo'],
cardNumbers: ['2.300', '259', '45', 'R$ 350,00']
}
}
}
</script>
Is there any way to also add the cardNumber using v-for? It works fine the way it is, but I wanna use the both props, not just the ItemCard
If I understood you correctly , try to return right number with index:
Vue.component('highlightCard', {
template: `
<div class="bg-light rounded p-2 px-5">
<h5> {{ cardNumber }}</h5>
<h3>{{ cardItem }}</h3>
</div>
`,
props: ['cardItem', 'cardNumber']
})
new Vue({
el: "#demo",
data () {
return {
cardItems: ['Impressões', 'Cliques', 'Conversões', 'Custo'],
cardNumbers: ['2.300', '259', '45', 'R$ 350,00']
}
},
methods: {
num(val) {
return this.cardNumbers[this.cardItems.findIndex(i => i === val)]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div class="row m-auto">
<highlight-card
v-for="(itemCard, index) in cardItems"
:key="index"
:card-item="itemCard"
:card-number="num(itemCard)"
class="col m-3"/>
</div>
</div>

How can I make row number in vue component?

I have two component vue
My first component like this :
<template>
<div>
<b-row>
<div class="pl-2 d-flex">
<div class="card-body">
<p class="mb-0 w-5 w-sm-100">Number</p>
<div class="w-30 w-sm-100">Description</div>
<div class="w-20 w-sm-100">Date</div>
<div class="w-10 w-sm-100">Modified By</div>
</div>
</div>
</b-row>
<b-row key="list">
<b-colxx xxs="12" class="mb-3" v-for="(item,index) in items" :key="index" :id="item.id">
<list-item
:key="item.id"
:data="item"
:index="index"
/>
</b-colxx>
</b-row>
...
<b-pagination-nav
...
>
</b-pagination-nav>
...
</div>
</template>
<script>
import ListItem from "./ListItem";
export default {
components: {
"list-item": ListItem
},
};
</script>
My second component / child component like this :
<template>
<b-card no-body>
<div class="pl-2 d-flex">
<div class="card-body">
<p class="mb-0 text-muted w-5">{{index+1}}</p>
<p class="mb-0 text-muted w-30">{{data.description}}</p>
<p class="mb-0 text-muted w-20">{{data.date}}</p>
<p class="mb-0 text-muted w-10">{{data.created_by}}</p>
</div>
</div>
</b-card>
</template>
<script>
export default {
props: ['data', 'index'],
}
</script>
I use index to give row number. But the problem is when I move the page to another page, the line number will return to number 1
How can I solve this problem?
Please help. Thanks
You can create computed property, add line numbers to your items and loop over it:
new Vue({
el: '#demo',
data() {
return {
items: [],
fields: [{key: "lineNumber", label: "Number",}, {key: "postId", label: "Post ID",}, {key: "id", label: "ID",}, {key: "name", label: "Name",}, {key: "email", label: "Email",}, {key: "body", label: "Body",},],
currentPage: 0,
perPage: 10,
totalItems: 0,
}
},
computed: {
itemsWithLineNumber() {
return this.items.map((item, idx) => {
return {
...item,
lineNumber: (this.currentPage - 1) * this.perPage + idx + 1,
};
});
},
},
methods: {
async fetchData() {
await axios
.get(
`https://jsonplaceholder.typicode.com/comments?_page=${this.currentPage}&_limit=${this.perPage}`
)
.then((res) => {
this.totalItems = 500;
this.items = res.data;
});
},
changePage(nr) {
this.pageNr = nr
}
},
watch: {
currentPage: {
handler: function (value) {
this.fetchData().catch((error) => {
console.error(error);
});
},
},
},
async mounted() {
await this.fetchData()
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" />
<script src="https://polyfill.io/v3/polyfill.min.js?features=es2015%2CIntersectionObserver" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js" integrity="sha512-odNmoc1XJy5x1TMVMdC7EMs3IVdItLPlCeL5vSUPN2llYKMJ2eByTTAIiiuqLg+GdNr9hF6z81p27DArRFKT7A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="demo">
<div>
<b-table
show-empty
:items="itemsWithLineNumber"
:fields="fields"
:current-page="currentPage"
:per-page="0"
></b-table>
<b-pagination
size="md"
:total-rows="totalItems"
v-model="currentPage"
:per-page="perPage"
></b-pagination>
</div>
</div>

Using multiple counters with one method

I am trying to keep my practice application as minimal as possible, how would I change my method to allow multiple counters to use one state variable but not change each counter when I increase or decrease?
<template>
<h1>Test Counter</h1>
<div class="container">
<div class="item">
<div class="itemName">Item 1</div>
<button #click="decreaseCount">Decrease</button>
<div class="value">{{ count }}</div>
<button #click="increaseCount">Increase</button>
</div>
<div class="item">
<div class="itemName">Item 2</div>
<button #click="decreaseCount">Decrease</button>
<div class="value">{{ count }}</div>
<button #click="increaseCount">Increase</button>
</div>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
count: 0,
};
},
methods: {
increaseCount() {
this.count += 1;
},
decreaseCount() {
this.count -= 1;
},
},
};
</script>
I have a very basic working example here where I have left the problem in the application - https://codesandbox.io/s/prod-hill-p6kum?file=/src/App.vue
I have tried to rename the state to it's own variable so each counter then has it's own state but when I have changed that the counter will not increase or decrease
Please check this out
<template>
<h1>Test Counter</h1>
<div class="container">
<div class="item">
<div class="itemName">Item 1</div>
<button #click="decreaseCount('counterOne')">Decrease</button>
<div class="value">{{ counterOne }}</div>
<button #click="increaseCount('counterOne')">Increase</button>
</div>
<div class="item">
<div class="itemName">Item 2</div>
<button #click="decreaseCount('counterTwo">Decrease</button>
<div class="value">{{ counterTwo }}</div>
<button #click="increaseCount('counterTwo')">Increase</button>
</div>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
counterOne: 0,
counterTwo: 0
};
},
methods: {
increaseCount(val) {
this[val] += 1;
},
decreaseCount(val) {
this[val] -= 1;
},
},
};
</script>
Another way
<template>
<h1>Test Counter</h1>
<div class="container">
<div class="item" v-for="(item, index) in items" :key="index">
<div class="itemName">{{item.name}}</div>
<button #click="decreaseCount(index)">Decrease</button>
<div class="value">{{ item.counter }}</div>
<button #click="increaseCount(index)">Increase</button>
</div>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
Items: [
{ name: 'Item 1', counter: 0 },
{ name: 'Item 1', counter: 0 },
.......
]
};
},
methods: {
increaseCount(val) {
this.items[val].counter += 1;
},
decreaseCount(val) {
this.items[val].counter -= 1;
},
},
};
</script>

Vue modal with a router

I am new to Vue. I am building a simple app that will list all countries and when you click on a particular country it shows you more details about the country. Idea is to open country details in a modal.
I'm stuck with displaying that modal. The modal opens, but in the background. It also opens a detail page.
CountryDetail.vue:
<script>
import axios from 'axios';
export default {
name: 'country-detail',
props: [ 'isDarkTheme' ],
data () {
return {
pending: false,
error: null,
countryInfo: null,
alpha3Code: [],
alpha3CodetoString: [],
}
},
mounted () {
this.pending = true;
axios
.get(`https://restcountries.eu/rest/v2/name/${this.$route.params.country}?fullText=true`)
.then((response) => {
(this.countryInfo = response.data)
this.alpha3CodetoString = this.alpha3Code.join(';');
})
.catch(error => (this.error = error ))
.finally( () => { this.pending = false });
},
filters: {
formatNumbers (value) {
return `${value.toLocaleString()}`
}
}
}
</script>
<template>
<modal v-model="show">
<div class="modal-mask" :class="{ darkTheme : isDarkTheme }" name="modal">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
<h1 v-if="error !== null">Sorry, an error has occurred {{error}}</h1>
<div class="loaderFlex"><div v-if="pending" class="loader"></div></div>
</slot>
</div>
<div v-for="country in countryInfo" class="countryTile modal-body" v-bind:key="country.id">
<slot name="body">
<img v-bind:src="country.flag" alt="Country Flag" class="flag">
<div class="country-details">
<h1>{{country.name}}</h1>
<div class="listDiv">
<ul>
<li><span>Population:</span> {{country.population | formatNumbers }}</li>
<li><span>Capital:</span> {{country.capital}}</li>
<li><span>Iso:</span> {{country.alpha3Code}}</li>
</ul>
<ul>
<li><span>Currencies:</span> {{country.currencies['0'].name}}</li>
<li><span>Languages:</span>
<span
v-for="(language, index) in country.languages"
v-bind:key="index"
class="languages">
{{language.name}}<span v-if="index + 1 < country.languages.length">, </span>
</span>
</li>
</ul>
</div>
</div>
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<a #click="$router.go(-1)" class="backBtn"><i class="fas fa-arrow-left" />Go Back</a>
</slot>
</div>
</div>
</div>
</div>
</modal>
</template>
Home.vue:
<script>
import axios from 'axios';
export default {
name: 'home',
props: [ 'isDarkTheme' ],
data () {
return {
pending: false,
error: null,
countryInfo: null,
search: '',
darkMode: false,
}
},
mounted () {
this.pending = true;
axios
.get('https://restcountries.eu/rest/v2/all')
.then(response => (this.countryInfo = response.data))
.catch(error => (this.error = error ))
.finally( () => { this.pending = false });
},
filters: {
formatNumbers (value) {
return `${value.toLocaleString()}`
}
},
computed: {
filteredCountries: function () {
return this.countryInfo.filter((country) => {
if (this.region === '' ) {
return country.name.toLowerCase().match(this.search.toLowerCase());
} else if (this.search !== '') {
return country.name.toLowerCase().match(this.search.toLowerCase());
} else {
return ('blbla');
}
})
}
},
}
</script>
<template>
<div class="home" :class="{ darkTheme : isDarkTheme }">
<div class="searchBar">
<div class="searchContainer">
<i class="fas fa-search searchIcon"></i>
<input
class="searchInput"
type="text"
v-model="search"
aria-label="Search for a country..."
placeholder="Search for a country..."
/>
<ul class="searchResults"></ul>
</div>
</div>
<h1 v-if="error !== null">Sorry, an error has occurred {{error}}</h1>
<div class="loaderFlex"><div v-if="pending" class="loader"></div></div>
<div v-if="countryInfo" class="tileGrid" #click="showModal = true">
<div v-for="country in filteredCountries" class="countryTile" v-bind:key="country.id">
<router-link
:to="{ name: 'country-detail', params: {country: country.name }}"
class="linkTile"
>
<img v-bind:src="country.flag" alt="Country Flag" class="flag">
<div class="text">
<h1>{{ country.name }}</h1>
</div>
</router-link>
</div>
</div>
</div>
</template>
The router-link will always redirect you to another page, because its basically <a href="..."> see here. You don't need router if you just want to show the detail on a modal, you could just add the modal component inside the Home.vue component, then bind the modal and the countryName with props, then pass them in when clicking a button.
Home.vue:
<template>
<div>
<button #click="showDetail">
Show Detail
</button>
<CountryDetail :countryName="countryName" :showModal="showModal"/>
<div>
</template>
<script>
import CountryDetail from './CountryDetail.vue'
export default {
name: 'Home',
components: { CountryDetail },
data: () => ({
countryName: '',
showModal: false,
}),
methods: {
showDetail() {
this.showModal = true;
},
},
}
</script>
And instead of making request on mounted, you could use watch to do something like watching for the showModal prop, and make request everytime it has a truthy value. Like this:
CountryDetail.vue:
<template>
<modal v-model="showModal">
<!-- modal content -->
</modal>
</template>
<script>
export default {
name: 'CountryDetail',
props: ['countryName', 'showModal'],
watch: {
'showModal': {
deep: true,
handler(val) {
if (val && this.countryName !== '') {
// Make request
}
}
}
}
}
</script>

Nuxt.js/Vue.js resuable modal

My question is about core logic for the thing below
1) I have a Section Component and a Modal Component
2) In the Section Component i have an array of objects with title and description
3) In the Modal Component i have a template for the modal (im using Bulma framework for the modal)
Now, in the Section i have 7 buttons with a v-for loop to assign 'is-active' class and on each button click an individual modal opens.
Question: How do i parse data into the modal ? I want my modal to be reusable. So at the start my modal is empty at all. On button1 click i have title1 descr1, on button2 title2 descr2 and etc
My code:
Section Component:
<template>
<base-section name="clusters">
<div class="section-map">
<button
class="section-btn"
v-for="(item, index) in sectionItems"
:key="index"
:class="[`section-btn-num${index + 1}`, {'is-active': item.state}]"
#click="toggleModal(item)"
>
<div class="section-btn__text">
{{ item.title }}
</div>
</button>
</div>
<div class="modal"
v-for="(item, index) in sectionItems"
:key="index"
:class="{'is-active': item.state}"
>
<div class="modal-background"></div>
<div class="modal-content">
</div>
<button
#click="item.state = false"
class="modal-close is-large"
aria-label="close">
</button>
</div>
</base-section>
</template>
<script>
import BaseSection from './Section';
import BaseModal from './Modal';
export default {
components: {
BaseSection,
BaseModal,
},
methods: {
toggleModal: (item) => {
item.state = !item.state;
}
},
data() {
return {
sectionItems: [
{
title: 'title1',
},
{
title: 'title2',
description: 'descr',
},
{
title: 'title3',
description: 'descr',
},
{
title: 'title4',
description: 'descr',
},
{
title: 'title5',
description: 'descr',
},
{
title: 'title6',
description: 'descr',
},
{
title: 'title7',
description: 'descr',
},
].map(item => ({ ...item, state: false }))
};
}
};
</script>
<styles>
/* are skipped */
</styles>
Modal Component:
<template>
<div class="modal">
<div class="modal-background"></div>
<div class="modal-content">
<div class="modal__header">
<slot name="modal__header">
</slot>
</div>
<div class="modal__body">
<slot name="modal__body">
</slot>
</div>
</div>
<button
#click="item.state = false"
class="modal-close is-large"
aria-label="close">
</button>
</div>
</template>
<script>
import ClustersSection from './ClustersSection';
export default {
components: {
ClustersSection,
},
props: {
}
},
};
</script>
<style lang="scss">
</style>
Since you already defined slots in your component you can pass data to it via slots. E.g.
<your-modal-component>
<template slot="modal__header">
header bla bla a
</template>
<template slot="modal__body">
body anything here like <p> hehe</p>
</template>
</your-modal-component>

Categories