Can someone show me an example of SwiperJs slider, using Vue3 option api, not composition Api?
I mean pure javascript with template inside the html.
like
<div>
<div slider container>
<div vfor>
</div>
</div>
</div>
You can use something like this:
<template>
<div
:id="id"
class="swiper-container"
>
<div
class="swiper-wrapper"
#click="$emit('click')"
>
<!-- swipe right template -->
<template v-if="direction === 'right'">
<v-list-item class="swiper-slide" />
<v-list-item class="swiper-slide">
<v-list-item-content>
<slot />
</v-list-item-content>
</v-list-item>
</template>
<!-- swipe left template -->
<template v-if="direction === 'left'">
<v-list-item class="swiper-slide">
<v-list-item-content>
<slot />
</v-list-item-content>
</v-list-item>
<v-list-item class="swiper-slide" />
</template>
</div>
</div>
</template>
<script>
import 'swiper/dist/css/swiper.min.css';
import { Swiper } from 'swiper/dist/js/swiper.esm.js';
export default
{
props:
{
id:
{
type: [String, Number],
required: true
},
direction:
{
type: String,
default: 'right',
validator: value => ['right', 'left'].indexOf(value) !== -1
}
},
mounted () {
const self = this;
const el = '#' + this.id;
const direction = this.direction;
// Initialize Swiper
const swiper = new Swiper(el, {
initialSlide: this.direction === 'left' ? 0 : 1,
resistanceRatio: 0,
speed: 150,
observer: true,
observeParents: true,
});
// Event will be fired after transition
swiper.on('transitionEnd', function () {
const activeIndex = this.activeIndex;
if ((direction === 'right' && activeIndex === 0) || (direction === 'left' && activeIndex === 1)) {
self.$emit('swiped');
// Destroy slider instance and detach all events listeners
this.destroy();
}
});
}
};
</script>
Use can use like this
<template>
<swiper
:slides-per-view="4"
:space-between="30"
#swiper="onSwiper"
#slideChange="onSlideChange"
class="default-slider"
>
<swiper-slide v-for="item in items" :key="item">
<img :src="item" />
</swiper-slide>
</swiper>
</template>
<script>
import { Swiper, SwiperSlide } from 'swiper/vue';
export default {
components: {
Swiper,
SwiperSlide,
},
data() {
return {
items: ['1.jpg','2.jpg','3.jpg','4.jpg','5.jpg','6.jpg','7.jpg']
}
}
}
</script>
Related
AppsTab.vue
<script>
export default {
props: {
tabList: {
type: Array,
required: true,
},
variant: {
type: String,
required: false,
default: () => "vertical",
},
},
data() {
return {
activeTab: 1,
};
},
};
</script>
<template>
<div
:class="{
'flex space-x-4': variant === 'horizontal',
}"
>
<ul
class="list-none bg-blue-900 bg-opacity-30 p-1.5 rounded-lg text-center overflow-auto whitespace-nowrap"
:class="{
'flex items-center mb-6': variant === 'vertical',
}"
>
<li
v-for="(tab, index) in tabList"
:key="index"
class="w-full px-4 py-1.5 rounded-lg"
:class="{
'text-blue-600 bg-white shadow-xl': index + 1 === activeTab,
'text-white': index + 1 !== activeTab,
}"
>
<label
:for="`${_uid}${index}`"
v-text="tab"
class="cursor-pointer block"
/>
<input
:id="`${_uid}${index}`"
type="radio"
:name="`${_uid}-tab`"
:value="index + 1"
v-model="activeTab"
class="hidden"
/>
</li>
</ul>
<template v-for="(tab, index) in tabList">
<div
:key="index"
v-if="index + 1 === activeTab"
class="flex-grow bg-white rounded-lg shadow-xl p-4"
>
<!-- <slot :name="`tabPanel-${index + 1}`" /> --> want to get data using loop inside tab
</div>
</template>
</div>
</template>
App.vue
< script >
import AppTabs from "./components/AppTabs";
export default {
components: {
AppTabs,
},
data() {
return {
tabList: ["Tab 1", "Tab 2", "Tab 3", "Tab 4"],
};
},
}; <
/script>
<template>
<div class="bg-gradient-to-r from-blue-300 to-blue-500 min-h-screen">
<app-tabs
class="w-11/12 lg:w-10/12 mx-auto"
:tabList="tabList"
variant="horizontal"
>
{{value}} // want to bind data inside tab
</app-tabs>
</div>
</template>
Expected Output
I am working on vertical tabs. Where the functionality is working fine. Here is the complete working code with static mock data https://codesandbox.io/s/vue-js-tabs-forked-we2cx?file=/src/App.vue
Now i want to create some mockdata inside of my data like 'tabList' and then, i want to display data dynamically when user clicked on tabs(including content -->tabs)
How to remove static data, which is inside slots and then only use data dynamically
To start with that, i am not sure, Where to start the looping to display data inside tabs dynamically with mock data?
You can set dynamic slot name using :slot="slotName" where slotName is a dynamic value
This can be achieved using a v-for aswell like below.
<template v-for="content in contentList" :slot="content.slot">
{{ content.content }}
</template>
Where contentList is your array something like below.
contentList: [
{ id: 1, slot: "tabPanel-1", content: "Content 1" },
{ id: 2, slot: "tabPanel-2", content: "Content 2" },
{ id: 3, slot: "tabPanel-3", content: "Content 3" },
{ id: 4, slot: "tabPanel-4", content: "Content 4" },
]
Working Fiddle
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>
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>
I currently have two active themes using Tailwind light-theme and dark-theme but I can't make it work within an external button component, just with the code and function inside the view.
This is just an example where the function inside the view is acceptable but I have a "real world" case where I need it working from the component, changing the class between dark/light mode.
Here is my "Home.vue" file importing ButtonA and ButtomB:
<template>
<div :class="theme" class="bg-background-primary">
<h1 class="text-4xl text-typo-primary">Test title</h1>
<!-- Inside Home.vue - WORKING -->
<button class="border border-gray-400 bg-blue-500 hover:bg-blue-700 text-white p-2 rounded" #click="toggleThemeOne()">Toggle Dark/Light</button>
<!-- Component with function outside - WORKING -->
<ButtonA msg="From component" #click.native="toggleThemeTwo()" />
<ButtonB msg="Full from component" />
</div>
</template>
<script>
// # is an alias to /src
import ButtonA from '#/components/ButtonA.vue'
import ButtonB from '#/components/ButtonB.vue'
export default {
name: 'Home',
components: {
ButtonA,
ButtonB
},
data() {
return {
theme: 'theme-light',
}
},
methods: {
toggleThemeOne() {
this.theme = this.theme === 'theme-light' ? 'theme-dark' : 'theme-light'
localStorage.setItem('theme', this.theme)
console.log('toggleThemeOne working');
console.log(this.theme)
},
toggleThemeTwo() {
this.theme = this.theme === 'theme-light' ? 'theme-dark' : 'theme-light'
localStorage.setItem('theme', this.theme)
console.log('toggleThemeTwo working');
console.log(this.theme)
},
}
}
</script>
Home.vue has a working button that's changing the theme
ButtonA
It has the HTML only and the function applied on the component
<template>
<div>
<button class="border border-gray-400 bg-blue-500 hover:bg-blue-700 text-white p-2 rounded"> {{ msg }} </button>
</div>
</template>
<script>
export default {
name: "ButtonComp",
props: [
'msg'
]
}
</script>
ButtonB
<template>
<div>
<button
class="border border-gray-400 bg-blue-500 hover:bg-blue-700 text-white p-2 rounded"
#click="toggleThemeTree()"
> {{ msg }} </button>
</div>
</template>
<script>
export default {
name: "ButtonComp",
props: [
'msg'
],
methods: {
toggleThemeTree() {
this.theme = this.theme === 'theme-light' ? 'theme-dark' : 'theme-light'
localStorage.setItem('theme', this.theme)
console.log('toggleThemeTree working');
console.log(this.theme)
},
},
}
</script>
This is the one that's not working. the function should change the :class on Home.vue but I only get the values on the console and the :class isn't working.
I did try with $emit and computed property before but It didn't work.
You should pass theme to ButtonB component in Home.vue:
<ButtonB msg="Full from component" :theme.sync="theme" />
Then in ButtonB component, emit the value back to parent on click:
<script>
export default {
name: "ButtonComp",
props: [
'msg',
'theme'
],
methods: {
toggleThemeTree() {
let theme = this.theme === 'theme-light' ? 'theme-dark' : 'theme-light' // Do not change this.theme directly
localStorage.setItem('theme', theme)
this.$emit('update:theme', theme)
},
},
}
</script>
I am trying to add the info icon in AG Grid header for displaying the tooltip when I hover on the icon. I have created the custom tooltip component which shows the tooltip when hovered but when I add the icon the default sorting and filters get removed.
import Vue from "vue";
export default Vue.extend({
template: `
<div>
<div>
{{ params.headerName }}
<v-tooltip bottom max-width="200">
<template v-slot:activator="{ on }">
<i v-on="on" class="custom-info info circle icon"></i>
</template>
<span>{{params.toolTipText}}</span>
</v-tooltip>
</div>
</div>
`,
data: function() {
return {
};
},
beforeMount() {},
mounted() {
// console.log("header components",params.value);
},
methods: {
},
}, );
**
Column Defs Code: **
this is the code
for column defs.
field: "ndc11",
filter: "agNumberColumnFilter",
headerComponent: 'customTooltipIcon',
headerComponentParams: {
headerName: "NDC11",
toolTipText: "NDC11"
},
pinned: "left",
cellClass: params => {
if (
params.data &&
params.data.ion_dispute_code &&
params.data.ion_dispute_code.length &&
(params.data.ion_dispute_code.includes("O") ||
params.data.ion_dispute_code.includes("N") ||
params.data.ion_dispute_code.includes("U") ||
params.data.ion_dispute_code.includes("G"))) {
return "validation-grid-cell-red"
}
},
cellRenderer: "ndc11Render",
sort: "asc"
},
because you're rewriting ag-grid header with you'r custom one, and not adding the sorting and filtering in it
here is an example how it should look like:
export default Vue.extend({
template: `
<div>
<div
v-if="params.enableMenu"
ref="menuButton"
class="customHeaderMenuButton"
#click="onMenuClicked($event)"
>
<i class="fa" :class="params.menuIcon"></i>
</div>
<div class="customHeaderLabel">{{ params.headerName }}</div>
<v-tooltip bottom max-width="200">
<template v-slot:activator="{ on }">
<i v-on="on" class="custom-info info circle icon"></i>
</template>
<span>{{ params.toolTipText }}</span>
</v-tooltip>
<div
v-if="params.enableSorting"
#click="onSortRequested('asc', $event)"
:class="ascSort"
class="customSortDownLabel"
>
<i class="fa fa-long-arrow-alt-down"></i>
</div>
<div
v-if="params.enableSorting"
#click="onSortRequested('desc', $event)"
:class="descSort"
class="customSortUpLabel"
>
<i class="fa fa-long-arrow-alt-up"></i>
</div>
<div
v-if="params.enableSorting"
#click="onSortRequested('', $event)"
:class="noSort"
class="customSortRemoveLabel"
>
<i class="fa fa-times"></i>
</div>
</div>
`;
data: function () {
return {
ascSort: null,
descSort: null,
noSort: null
};
},
beforeMount() {},
mounted() {
this.params.column.addEventListener('sortChanged', this.onSortChanged);
this.onSortChanged();
},
methods: {
onMenuClicked() {
this.params.showColumnMenu(this.$refs.menuButton);
},
onSortChanged() {
this.ascSort = this.descSort = this.noSort = 'inactive';
if (this.params.column.isSortAscending()) {
this.ascSort = 'active';
} else if (this.params.column.isSortDescending()) {
this.descSort = 'active';
} else {
this.noSort = 'active';
}
},
onSortRequested(order, event) {
this.params.setSort(order, event.shiftKey);
}
}
});
example was taken from ag-grid documentation: https://www.ag-grid.com/javascript-grid-header-rendering/#vueCellEditing
also here https://www.ag-grid.com/javascript-grid-header-rendering/#vueCellEditing you could find more details related to how header components works and custom header components should work