Unable to access vue data in basic modal - javascript

I am trying to loop and reference vue data values within a modal and am getting an error that the field needs to be reactive, despite the values being provided in the component initialization. Any idea why this is?
Fiddle:
https://jsfiddle.net/mwLbw11k/2618/
JS
// register modal component
Vue.component('modal', {
template: '#modal-template'
})
// start app
new Vue({
el: '#app',
data: {
myData: [{'first': 'Matt', 'last': 'Smith'}, {'first': 'Tom', 'last': 'Brady'}],
showModal: false
}
})
HTML
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
<ul>
<li v-for="user in myData"> {{ user.first }} {{ user.last }}</li>
</ul>
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
<!-- app -->
<div id="app">
<button id="show-modal" #click="showModal = true">Show Modal</button>
<!-- use the modal component, pass in the prop -->
<modal v-if="showModal" #close="showModal = false">
</modal>
</div>
Vue Warning
[Vue warn]: Property or method "myData" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.

Yous should add myData to modal component props
Vue.component('modal', {
template: '#modal-template',
props: ['myData']
})
and then pass it from parent like this:
<modal v-if="showModal" :my-data="myData" #close="showModal = false">
</modal>

corrected demo from Bsalex
<modal v-if="showModal" :my-data="myData" #close="showModal = false">

Related

pass component according to v-if - Vue.JS

I am trying to create a parent component with all kinds of graphics, and depending on what I need to pass on a specific graphic (raise a child) to another component, but I don't necessarily know how I would dynamically do it for the user.
my html charts (chart.vue)
<template>
<div >
<bars v-if="type == 'bar'">
<div class="row gutter-sm">
<div class="col-md-3">
<apexchart ref="chart1" type="bar" :options="options" :series="updatedData"></apexchart>
</div>
</div>
<div class="col-md-3">
<apexchart ref="chart3" type="bar" :options="options3" :series="updatedData"></apexchart>
</div>
</bars>
<lines v-if="type == 'line'">
<div class="row gutter-sm">
<div class="col-md-3">
<apexchart ref="chart4" type="line" :options="options4" :series="updatedData"></apexchart>
</div>
</div>
</lines>
</div>
<template>
If I wanted to pass on bar graphics to my menu.vue, would it be like this?
my html menu
<template>
<div >
<table class="data-table-2" style="min-width: 800px">
<charts type="bar"/>
</table>
</div>
<template>
scripts menu
<script>
import charts from "./charts.vue"
export default {
name: 'menu',
components: {
charts
}
}
</script>
The way you pass values from the parent component to the child component is wrong
<template>
<div >
<table class="data-table-2" style="min-width: 800px">
<charts :type='bar'/>
<charts :type='line'/>
<charts />
</table>
</div>
<template>
child component receiving value, and you can set a default value:
// init in child component
props: {
type: {
type: String,
default: 'bar' // set a default value
}
},
data(){
...
}
then you can use type in child component
<template>
<div >
<bars v-if"type == 'bar'">
<div class="row gutter-sm">
<div class="col-md-3">
<apexchart ref="chart1" type="bar" :options="options" :series="updatedData"></apexchart>
</div>
</div>
<div class="col-md-3">
<apexchart ref="chart3" type="bar" :options="options3" :series="updatedData"></apexchart>
</div>
</bars>
<lines v-if"type == 'line'">
<div class="row gutter-sm">
<div class="col-md-3">
<apexchart ref="chart4" type="line" :options="options4" :series="updatedData"></apexchart>
</div>
</div>
</lines>
</div>
<template>
You need to define a Props in child component.
//Child Component Script
export default ChildComponent {
data() {
return {}
},
props: {
type: string,
}
}
In parent component you need to call child component and pass type data like this
//parent component
<template>
<div>
<child-component :type="line"/>
</div>
</template>

Vue.js 3 - inserting component into slot

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.

Rendering VUE slot only if content matches specific value

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>

How to pass a dynamic variable to a 'component' and then display it - VueJS

I can not display a variable (which is the link of an image) in my component I am lost ...
(To help you, I'm in a table and click on the icon that opens a pdf)
Thanks you !
Vue.component('modal', {
template: '#modal-template-pdf',
props: {
lien: String
}
});
let appCompta = new Vue({
data: {
showModalActive: false,
currentModalPdfLink: '',
}
}).$mount('#'+id+' #app-compta')
<img slot="apercu" slot-scope="props" class="iconeged main" src="images/ico_pdf.png"
v-bind:currentModalPdfLink='props.row.imglien'
#click="showModalActive = true"
>
<modal v-if="showModalActive" #close="showModalActive = false" v-bind:lien="currentModalPdfLink">
<h3 slot="header">Aperçu PDF</h3>
</modal>
<script type="text/x-template" id="modal-template-pdf">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
{{currentModalPdfLink}}
<iframe height='100%' width='100%' frameborder='0' :src="currentModalPdfLink"/>
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button class="modal-default-button" #click="$emit('close')">
FERMER
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
I assume that currentModalPdfLink is your pdf-link?
The name of the prop that you are passing is src so you need to refer to it in your component with the same name:
Vue.component('modal', {
template: '#modal-template-pdf',
props: ['src']
});

How to use BootstrapVue - Modal inside v-for loop?

I am trying to use BootstrapVue - Modal inside v-for loop and the only problem is with modal directive ( v-b-modal.modal1 ) on a modal button. modal1 should be the name of modal id and since I am using a loop I am passing index in modal for example modal + index, but I don't know how to change buttons directive to be v-b-modal-modal1 ... v-b-modal-modal5.
This is modal component
<template>
<div>
//This v-b-modal.modal1 should be same as modalId
<b-btn v-b-modal.modal1>Banka: {{ data.offer.client_name }}</b-btn>
<!-- Modal Component -->
<b-modal :id="modalId" title="Oferta">
<p clas="my-4">Kampanja: {{ data.offer.campaign_name }}</p>
<p clas="my-4">Norma e interesit: {{ data.offer.interest_rate_nominal }}</p>
<p clas="my-4">*Shpenzimet Administrative: {{ data.offer.admin_fee }}</p>
<p clas="my-4">Kësti Mujor: {{ data.offer.monthly_payment }}</p>
</b-modal>
</div>
</template>
<script>
export default {
props: ['data'],
computed:{
modalId(){
return 'modal' + this.data.i;
}
}
}
</script>
Here is a method that uses modal
<tbody>
<tr v-for="(offer, i) in offers">
<td>
<app-show-details :data="{offer, i}"></app-show-details>
</td>
</tr>
</tbody>
BootStrapVue provides more than one pattern to achieve modal, so you don't have to insist on directive modifiers, I think using directive value fits your requirement here perfectly. Check sample code below.
new Vue({
el: '#app',
methods: {
modalId(i) {
return 'modal' + i;
}
}
});
// or check jsfiddle here: https://jsfiddle.net/5sv805ho/
<script src="https://unpkg.com/vue#2.5.2/dist/vue.min.js"></script>
<link href="https://unpkg.com/bootstrap#next/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<div v-for="i in 3">
<b-btn v-b-modal="modalId(i)">Launch demo modal</b-btn>
<b-modal :id="'modal' + i" title="Bootstrap-Vue">
<p clas="my-4">Hello from modal {{ i }}!</p>
</b-modal>
</div>
</div>
By the way, you can also use show() and hide() component methods.

Categories