I am still a beginner at vue and quasar and I would like to animate this Article Container.
<template>
<div class="row align-center q-gutter-md">
<ArticleCard v-for="(article, index) in currentSlice" :article="article" :key="index"/>
</div>
<q-btn rounded label="next" #click="changeSlice" class="self-center"></q-btn>
</template>
<script setup>
import { ref } from 'vue';
import ArticleCard from './ArticleCard.vue';
const props = defineProps({
articles: Array,
articlesShowed: Number
});
const currentEnd = ref(props.articlesShowed);
const currentSlice = ref(props.articles.slice(0, currentEnd.value));
const changeSlice = () => {
currentSlice.value = props.articles.slice(currentEnd.value, currentEnd.value + props.articlesShowed);
currentEnd.value += props.articlesShowed;
}
</script>
and this is my card
<template>
<transition
:duration="1000"
enter-active-class="animated fadeIn"
leave-active-class="animated fadeOut"
>
<q-card class="q-pa-md">
<q-card-section>
<div class="text-h6">{{ props.article.title }}</div>
<q-separator inset></q-separator>
</q-card-section>
<q-card-section>{{ article.description }}</q-card-section>
</q-card>
</transition>
</template>
<script setup>
import { ref } from "vue";
const props = defineProps({
article: Object
});
</script>
For some reason this transition does not work. Technically, I believe that this happens because the card is not toggled, I am merely changing the content inside of it. Could someone pin down the problems, and give me some hints or advices?
Related
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>
I created a page to show all events that I created, the problem is that when entering the page, no event is shown, only the add events button. And as soon as I hit enter in the IDE and add an empty line anywhere in the code, the list magically appears. Can anyone explain to me the reason for this?
I'm using Quasar and Firebase, I'll let my HTML and Javascript code here if anyone has any idea why this is happening
HTML
<template>
<q-page class="flex flex-center bg-primary column">
<q-btn
to="/adicionar-evento"
color="grey-9"
padding="16px 50px"
class="q-mt-lg"
>
<q-icon left size="2.5em" name="add_circle" />
Adicionar um novo evento
</q-btn>
<div v-for="evento in eventos" :key="evento.id" class="partidaDiv row">
<div class="column colunaPartida">
<h1 class="dataHorario">{{ evento.data.slice(0, 5) }}</h1>
<h1 class="dataHorario">{{ evento.horaInicio }}</h1>
</div>
<div class="column colunaPartida2">
<h1 class="dataHorario">{{ evento.quadra.label }}</h1>
<h1 class="textoLocal">{{ evento.quadra.value }}</h1>
<h1 class="textoLocal">Cachoeirinha, Rio Grande do Sul</h1>
</div>
<div class="column colunaPartida3">
<q-icon name="person" color="white" size="2rem" />
<h1 class="dataHorario">
{{ evento.numeroVagasPreenchidas }}/{{ evento.numeroVagasJogadores }}
</h1>
</div>
</div>
<q-item>
<h1></h1>
</q-item>
</q-page>
</template>
Javascript
<script>
import db from "src/boot/firebase";
import {
collection,
getDocs,
orderBy,
query,
addDoc,
onSnapshot,
deleteDoc,
doc,
getDoc,
updateDoc,
} from "firebase/firestore";
export default {
setup() {
return {
name: "IndexPage",
eventos: [],
};
},
async mounted() {
const ordersRef = collection(db, "eventos");
const q = query(ordersRef, orderBy("data"));
const unsubscribe = onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach((change) => {
let eventoChange = change.doc.data();
eventoChange.id = change.doc.id;
if (change.type === "added") {
this.eventos.unshift(eventoChange);
}
});
});
},
};
</script>
Figured out, I was using setup() instead of data().
I tried to create a chart with vue 3 and and google charts. Chart is created with vue-google-charts package. But i couldn't able to create control wrapper for it. I want to include RangeSliderFilter to my charts. Here is my code.
<script setup>
import AppLayout from '#/Layouts/AppLayout.vue';
import { Head, Link } from '#inertiajs/inertia-vue3';
import { ref, onMounted, nextTick } from "vue";
import { GChart } from 'vue-google-charts'
const props = defineProps({
data: Object
})
const chartData = ref([])
const rangeSlider = ref();
onMounted(() => {
chartData.value[0] = props.data.header_array;
props.data.msg_array.map((val, index) => {
val[0] = new Date(val[0])
chartData.value.push(val)
})
});
const chartOptions = ref({
title: "Temperature History",
curveType: 'function',
height: 700,
})
</script>
<template>
<Head title="Temperature History" />
<AppLayout>
<div class="py-12">
<div class="mx-auto sm:px-6 lg:px-8">
<div class="shadow-md sm:rounded-lg bg-white">
<div class="p-4">
<GChart v-if="chartData.length > 0" type="LineChart" :data="chartData"
:options="chartOptions" />
<div id="filter_div"></div>
</div>
</div>
</div>
</div>
</AppLayout>
</template>
How I can do this? Is there any good chart package for this?
What I'm trying to achieve
I'm trying to pass Component into the slot.
The question/information
How do I pass the Component into the slot so that It will be rendered? This works fine as long as I pass strings / plain html.
Additional question
If this is not possible - then how can I pass component into other component with the structure like below?
Parent
Template code
<template>
<card-with-title card-title="Title">
<template #card-body>
<row-fontawesome-icon-with-text v-for="mailDto in lastProcessedEmails"/>
</template>
</card-with-title>
</template>
Script code - the important part
<script>
import SymfonyRoutes from '../../../../../core/symfony/SymfonyRoutes';
import GetLastProcessedEmailsResponseDto from '../../../../../core/dto/api/internal/GetLastProcessedEmailsResponseDto';
import MailDto from '../../../../../core/dto/modules/mailing/MailDto';
import CardWithTitleComponent from '../../../../base-layout/components/cards/card-with-title';
import RowFontawesomeIconWithTextComponent from '../../../../other/row-fontawesome-icon-with-text';
export default {
components: {
'card-with-title' : CardWithTitleComponent,
'row-fontawesome-icon-with-text' : RowFontawesomeIconWithTextComponent,
},
<...>
Child
<!-- Template -->
<template>
<div class="col-12 col-lg-4 mb-4">
<div class="card border-light shadow-sm">
<div class="card-header border-bottom border-light">
<h2 class="h5 mb-0">{{ cardTitle }}</h2>
</div>
<div class="card-body">
<slot name="card-body"></slot>
<slot></slot>
</div>
</div>
</div>
</template>
<!-- Script -->
<script>
export default {
props: [
"cardBody",
"cardStyle",
"cardTitle"
],
}
</script>
I did research about the question, I've seen in documentation how does the named slots work like, but non of the posts / blogs entries answer / solve my Problem.
Examples of checked resources:
https://www.smashingmagazine.com/2019/07/using-slots-vue-js/
How to insert named slots into parent components
https://medium.com/js-dojo/vue-named-slot-shorthand-8a920358e861
https://v3.vuejs.org/guide/component-slots.html
https://medium.com/#norton.seanm/vue-js-slots-8a274c80450e
I've found the solution... It's pretty much... terrifying.
Vue is not checking if the array is empty, on the v-for it tries to loop over and then throws error.
Personally, from other languages / frameworks - this should not happen.
But well, this is the solution:
<!-- Template -->
<template>
<card-with-title card-title="Title">
<template #card-body>
<div v-if="[lastProcessedEmails.length]">
<row-fontawesome-icon-with-text v-for="mailDto in lastProcessedEmails">
<template #icon>
<i class="font-weight-bold">
<i v-if="mailDto.status === mailStatusSent" :class="fontawesomeIconClassesSent"></i>
<i v-else-if="mailDto.status === mailStatusPending" :class="fontawesomeIconClassesPending"></i>
<i v-else :class="fontawesomeIconClassesError"></i>
</i>
</template>
<template #title>
{{ mailDto.subject }}
</template>
<template #title-context>
{{ mailDto.created }}
</template>
</row-fontawesome-icon-with-text>
</div>
</template>
</card-with-title>
</template>
The whole problem was that:
data(){
return {
lastProcessedEmails: [],
}
},
The lastProcessedEmails is updated via Axios Call.
I have two routes that render the same component but with different data coming from an API.
This component has a child component called <base-section> that has a v-if directive that checks if a specific slot has content or not (because if it has no content, I don't want the slot to show).
However, there might be more than one instance of this child component on the same parent component, and therefore, if one of the instances has content in the slot, but the other one doesn't, VUE will automatically assume that all slots have content.
Therefore, I would like to know if there is any way of checking the specific slot content of each instance and then compare it with the data coming from the API. Please find my code below:
Parent component (Content.vue)
<template>
<base-section
v-for="entry in this.entries"
:key="entry.title"
lang="lang-markup"
:title="entry.title"
>
<template v-slot:content>
<span v-html="entry.content"></span>
</template>
<template v-slot:examples>
<div v-html="entry.examples"></div>
</template>
<template v-slot:code>
{{ entry.code }}
</template>
</base-section>
</template>
Child component (BaseSection.vue)
<template>
<hr class="my-6" />
<h4 class="text-salmon">{{ title }}</h4>
<section>
<div class="section-sm txt-justify" v-if="this.$slots.content">
<slot name="content"></slot>
</div>
<span class="medal bg-light text-dark code-medal">Examples</span>
<div class="section-sm border-light-1 mb-3">
<slot name="examples"></slot>
</div>
<span class="medal text-dark code-medal">Code</span>
<pre :class="lang + ' border-light-1 bg-light'">
<code><slot name="code"></slot></code>
</pre>
</section>
</template>
The data coming from the API follows this structure:
getData() {
const url = this.apiUrl + this.$route.name + this.apiToken
fetch(url)
.then((collection) => collection.json())
.then((collection) => {
const entries = [];
this.entries = [];
for (const id in collection.entries) {
if (collection.entries[id].Version === this.byteVersion) {
entries.push({
title: collection.entries[id].Title,
content: collection.entries[id].Content,
examples: collection.entries[id].Examples,
code: collection.entries[id].Code,
});
}
}
this.entries = entries;
});
}
Thank you very much for your help!
Regards,
T.
Maybe you can pass the "entry.content" into your BaseSection component. and v-if the entryContent.
Parent component (Content.vue)
<template>
<base-section
v-for="entry in this.entries"
:key="entry.title"
lang="lang-markup"
:title="entry.title"
:entryContent="entry.content"
>
<template v-slot:content>
<span v-html="entry.content"></span>
</template>
<template v-slot:examples>
<div v-html="entry.examples"></div>
</template>
<template v-slot:code>
{{ entry.code }}
</template>
</base-section>
</template>
Child component (BaseSection.vue)
<div class="section-sm txt-justify" v-if="entryContent">
<slot name="content"></slot>
</div>
Or you can v-if your content template
<template v-slot:content v-if="entry.content">
<span v-html="entry.content"></span>
</template>