Vue: pass instantiated component to slot - vuejs2

I'm writing a component that renders a text. When a word starts with '#' it's a user's reference (like in twitter), and I must create a tooltip with the user's info.
This is how I instantiate the user's info component (this works fine, I'm using it in other places of the app):
const AvatarCtor = Vue.extend(AvatarTooltip);
let avatarComponent = new AvatarCtor({
propsData: {
user: user
}
});
This is the TooltipWrapper component:
<template>
<el-tooltip>
<slot name="content" slot="content"></slot>
<span v-html="text"></span>
</el-tooltip>
</template>
<script>
import {Tooltip} from 'element-ui';
export default {
name: "TooltipWrapper",
components: {
'el-tooltip': Tooltip
},
props: {
text: String
}
}
</script>
And this is how I wire it up all together:
const TooltipCtor = Vue.extend(TooltipWrapper);
const tooltip = new TooltipCtor({
propsData: {
text: "whatever"
}
});
tooltip.$slots.content = [avatarComponent];
tooltip.$mount(link);
This doesn't work. But if I set some random text in the content slot, it works fine:
tooltip.$slots.content = ['some text'];
So my problem is that I don't know how to pass a component to the slot. What am I doing wrong?

this.$slots is VNodes, but you assign with one component instance.
Below is one approach (mount the component to one element then reference its vnode) to reach the goal.
Vue.config.productionTip = false
const parentComponent = Vue.component('parent', {
template: `<div>
<div>
<slot name="content"></slot>
<span v-html="text"></span>
</div>
</div>`,
props: {
text: {
type: String,
default: ''
},
}
})
const childComponent = Vue.component('child', {
template: `<div>
<button #click="printSomething()">#<span>{{user}}</span></button>
<h4>You Already # {{this.clickCount}} times!!!</h4>
</div>`,
props: {
user: {
type: String,
default: ''
},
},
data(){
return {
clickCount: 1
}
},
methods: {
printSomething: function () {
console.log(`already #${this.user} ${this.clickCount} times` )
this.clickCount ++
}
}
})
const TooltipCtor = Vue.extend(parentComponent)
const tooltip = new TooltipCtor({
propsData: {
text: "whatever"
}
})
const SlotContainer = Vue.extend(childComponent)
const slotInstance = new SlotContainer({
propsData: {
user: "one user"
}
})
slotInstance.$mount('#slot')
tooltip.$slots.content = slotInstance._vnode
tooltip.$mount('#link')
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="link">
</div>
<div style="display:none"><div id="slot"></div></div>

Related

Cannot pass an object to a new route using props

I am working on a project where a user fills in a quiz and the results need to be passed to a new page. The quiz has the Quiz component, where the quiz is taken, and the QuizFinished component, where the user can go to the Results page. I have been able to pass the quiz answers to QuizFinished, but the problem lies in passing the answers from QuizFinished to QuizResults. I have copied some code from here which is from this stackoverflow question, but the code does not work.
Am I missing something here?
Any help would be much appreciated.
Quiz.vue
<template>
<div class="quiz-background py-5" id="mood-finder">
<b-container class="text-center">
<h1 class="">Mood finder</h1>
<component
<!-- This event is triggered when a question is answered in Question.vue, that works -->
v-on:click="_answerQuestion($event)"
<!-- Going from the startpage to the quiz -->
v-on:start="this.startQuiz"
v-bind:is="component"
<!-- Binds the answers to be read in QuizResult -->
:answers="answers"
ref="question"
/>
<b-row class="d-flex justify-content-center">
<b-col cols="12" md="8" class="pt-3">
<!-- A progress bar -->
<b-progress
variant="primary"
aria-label="Voortgang van de Moodfinder"
:value="progress"
:max="maxProgress"
></b-progress>
</b-col>
</b-row>
</b-container>
</div>
</template>
<script>
import quizStart from "./QuizStart.vue";
import question from "./Question";
import strawberry from "../assets/strawberry.webp";
import banana from "../assets/banana.webp";
import quizFinished from "./QuizFinished.vue";
export default {
name: "app",
components: {
quizStart,
question,
quizFinished,
},
data() {
return {
questionIndex: 0,
<!--The array with the answers. It is put in an object to circumvent the array limitation by Vue: https://github.com/vuejs/vue/issues/1798 -->
answers: {array: []},
component: "quizStart",
strawberry: strawberry,
banana: banana,
progress: 0,
maxProgress: 99,
};
},
methods: {
/**
* #description
* saved pressed answer text in answers array
*/
_answerQuestion(chosenItem) {
this.answers.array.push(chosenItem);
this.switchQuestion();
},
startQuiz(){
this.component= "question"
},
/**
* #description switches the questions when the user performs an action in the quiz
* it swaps out the quiz images, text and quiz question for each question.
*/
switchQuestion() {
/**
* questionIndex stands for the question the user would like to go to.
* So e.g questionIndex = 1 is going to the second question (counting from 0)
*/
this.questionIndex++;
switch (this.questionIndex) {
case 0:
this.progress = 0;
break;
// //For question 1, see Question.Vue data field
case 1:
this.$refs.question.setQuestion("Question 2");
this.$refs.question.setItems(
{ name: "strawberry", variety: "sweet" },
{ name: "strawberry", variety: "sweet" },
{ name: "strawberry", variety: "sweet" },
{ name: "strawberry", variety: "sweet" }
);
this.$refs.question.setImage(
this.strawberry,
this.strawberry,
this.strawberry,
this.strawberry
);
this.progress = 33;
break;
case 2:
console.log(this.questionIndex)
this.$refs.question.setQuestion("Question 3");
this.$refs.question.setItems(
{ name: "banana", variety: "sweet" },
{ name: "banana", variety: "sweet" },
{ name: "banana", variety: "sweet" },
{ name: "banana", variety: "sweet" }
);
this.$refs.question.setImage(
this.banana,
this.banana,
this.banana,
this.banana
);
this.progress = 66;
break;
case 3:
this.progress = 99;
//Goes to the quizFinished component
this.component = quizFinished;
}
},
},
};
</script>
//removed the styles
QuizFinished.vue:
<template>
<b-row class="d-flex justify-content-center py-3">
<b-col class="col-6 col-lg-2">
<p class="text-white">
De Mood finder is gemaakt. Je resultaten staan klaar!
</p>
<!-- <router-link :to="{ path: '/results/', params: { answers: answers } }"
><button class="btn btn-primary" type="button">
Verder naar resultaten
</button></router-link
> -->
<br />
<router-link :to="{ name: 'results', params: { params: answers} }"
>Go to results</router-link>
<!-- For testing purposes, the answers are shown here as: { "array": [{object1} etc.]} -->
{{ answers }}
</b-col>
</b-row>
</template>
<script>
export default {
name: "quizFinished",
props: {
//For reasoning behind type: Object, see previous codeblock
answers: { type: Object, required: false },
},
};
</script>
//removed styles
QuizResult.vue
<!-- Html here -->
<script>
export default {
props: {
params: { type: Object, required: false },
},
mounted() {
//returns undefined, adding a html object like this: {{params}} returns nothing
console.log(this.params);
}
};
</script>
//removed styles
main.js:
import Vue from 'vue';
import App from './App.vue';
import VueRouter from 'vue-router';
import './registerServiceWorker';
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue';
import 'bootstrap-vue/dist/bootstrap-vue.css';
import '../src/scss/bootstrap.css';
// Import for fontAwesome : svg-core, brand icons and vue
import { library } from '#fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome';
import { fab } from '#fortawesome/free-brands-svg-icons';
import VueMeta from 'vue-meta';
library.add(fab);
// fontawesome component neccesary to call
Vue.component('font-awesome-icon', FontAwesomeIcon, fab);
// Make BootstrapVue available throughout your project
Vue.component('font-awesome-icon', FontAwesomeIcon, fab);
Vue.use(BootstrapVue);
Vue.use(IconsPlugin);
Vue.use(VueRouter);
Vue.use(VueMeta);
import results from './components/pages/Results';
import index from './components/pages/Index';
import pageNotFound from './components/pages/PageNotFound';
import webshop from './components/pages/Webshop';
const resultsRoute = { path: '/results', name: 'results', component: results, props: true, };
const indexRoute = { path: '/', name: 'index', component: index };
const pageNotFoundRoute = { path: '*', name: 'pageNotFound', component: pageNotFound };
const webshopRoute = { path: '/webshop', name: 'webshop', component: webshop };
const routes = [resultsRoute, indexRoute, pageNotFoundRoute, webshopRoute];
const router = new VueRouter({
routes,
mode: 'history',
});
Vue.config.productionTip = false;
new Vue({
router,
render: function(h) {
return h(App);
},
}).$mount('#app');
Here's a very quick snippet to show quiz using VueRouter:
const QuizStart = {
template: `
<div><router-link :to="'/question/0'">START</router-link></div>
`
}
const QuizQuestion = {
props: ['qidx', 'questions'],
computed: {
question() {
return this.questions[this.qidx]
},
},
methods: {
setAnswer(val) {
this.$emit('set-answer', {
id: this.qidx,
answer: val
})
let next = ''
if (Number(this.qidx) < this.questions.length - 1) {
next = `/question/${ Number(this.qidx) + 1 }`
} else {
next = '/result'
}
this.$router.push(next)
},
},
template: `
<div>
{{ question.text }}:<br />
<button #click="() => setAnswer(1)">YES</button> | <button #click="() => setAnswer(-1)">NO</button>
</div>
`
}
const QuizResult = {
props: ['questions'],
computed: {
computedResult() {
return this.questions.reduce((a, c) => a + c.answer, 0)
}
},
template: `
<div>
Your score is {{ computedResult }}
</div>
`
}
const routes = [{
path: '/',
component: QuizStart
},
{
path: '/question/:qidx',
component: QuizQuestion,
props: true
},
{
path: '/result',
component: QuizResult
},
]
const router = new VueRouter({
routes // short for `routes: routes`
})
new Vue({
el: "#app",
components: {
QuizStart,
QuizQuestion,
QuizResult,
},
router,
data() {
return {
questions: [{
text: 'Is this the first question?',
answer: null,
},
{
text: 'Is this the second question?',
answer: null,
},
{
text: 'Is this the third question?',
answer: null,
},
],
}
},
methods: {
setAnswer({
id,
answer
}) {
this.questions[id].answer = answer
},
},
template: `
<div>
<div>
QUESTIONS STATE: {{ questions }}
</div>
<hr />
<router-view
:questions="questions"
#set-answer="(data) => setAnswer(data)"
></router-view>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app"></div>
You can see that
the whole quiz state is tracked in one place: the question components do nothing but display the question and emit the answer back to the parent to update the state;
the questions are passed down from the parent; the route param (:qidx) is used to choose which question to display;
when the last question is reached, the next step is automatically the result page; but, because ALL the pages in the router-view receive the state of the questions, it's easy to calculate a result based on any calculation rule you might need.
I hope this helps you in a "smooth quiz-flow" :)
What I ended up doing is pushing to the router directly like this:
In the button:
v-on:click="toResults()"
And in methods:
toResults() {
this.$router.push({
name: "resultsRoute",
params: {
items: this.answers,
}
I was then able to get the items on the other side like this:
vm.$route.params.items

How to update data from vue-tables-2 after action from Template?

I'm using a custom component as a column on vue-tables-2, to do that I'm using a vue-component as described here: vue-components
I've created a button that opens a modal to the user confirm some information, and after that I make a request to the backend and the record is changed on the database.
Now I want to refresh the data on the table, but I don't know how to do that. The documentation said about using the $ref, but this is not an option because my component is not the parent.
How can I do that?
Links to the code:
Component using 'vue-tables-2'
<template>
<div>
<div id="payment">
<input type="checkbox" v-model="onlyPending" #change="filterPay()">Apenas pendentes</input>
<v-server-table url="/api/payments" :columns="columns" :options="options" ></v-server-table>
</div>
</div>
</template>
<script>
import pay from './ModalConfirmPay.vue'
import {Event} from 'vue-tables-2';
export default {
name: "AeraListPayment",
props: ['groupId'],
data: function(){
let groupId = this.groupId;
return {
columns: ['name','value','course','due_date','paid','installment','pay'],
options: {
responseAdapter : function(data) {
data.data = data.data.map(payment => {
payment.paid = payment.paid ? "pago" : "pendente";
return payment;
})
return data;
},
headings: {
installment: 'Parcela',
paid: 'Status',
value: 'Valor',
due_date: 'Vencimento',
pay: 'Ação',
course: 'Curso',
name: 'Nome'
},
templates : {
pay
},
customFilters: ['onlyPending','groupId'],
initFilters:{groupId:groupId,onlyPending:true}
},
onlyPending: true
}
},
methods: {
filterPay(){
Event.$emit('vue-tables.filter::onlyPending', this.onlyPending);
}
}
}
</script>
Component that is being used as a custom column:
<template>
<div>
<button #click.prevent="show">Pagar</button>
<modal :name="modalName">
<p>Confirma o pagamento de {{data.value}} ?</p>
<p>Parcela: {{data.installment}}</p>
<p>Vecimento: {{data.due_date}}</p>
<button #click.prevent="pay">Confirmar</button>
<button #click.prevent="hide">Cancelar</button>
</modal>
</div>
</template>
<script>
import PaymentService from '../../services/PaymentService'
let service = new PaymentService();
export default {
name:"ModalConfirmPay",
props: ["data"],
computed: {
modalName: function () {
// `this` aponta para a instância Vue da variável `vm`
return `confirm-pay-${this.data.clientGroup_id}-${this.data.installment}`
}
},
methods: {
show () {
this.$modal.show(this.modalName);
},
pay ( ) {
service.pay(this.data)
.then(this.hide());
},
hide () {
this.$modal.hide(this.modalName);
}
}
}
</script>
First, defined an EventBus if you don't have
EventBus.vue
import Vue from 'vue'
export default new Vue()
In ListPayment.vue, import EventBus and listen for refresh-table event. Note that I add ref="table" to vue-tables-2 element
<template>
<v-server-table ref="table" ... />
</template>
<script>
import EventBus from './EventBus.vue'
export default {
mounted() {
EventBus.$on('refresh-table', this.refreshTable)
},
beforeDestroy() {
EventBus.$off('refresh-table', this.refreshTable)
},
methods: {
refreshTable() {
this.$refs.table.refresh();
}
}
}
</script>
Finally, emit event in modal
pay() {
service.pay(this.data)
.then(() => {
EventBus.$emit('refresh-table')
})
.then(this.hide());
}

Fetch data in component on initiation using parameters from Vuex store

I am new to Vue and am trying to build a simple movie app, fetching data from an API and rendering the results. I want to have an incremental search feature. I have an input field in my navbar and when the user types, I want to redirect from the dashboard view to the search results view. I am unsure of how to pass the query params from the navbar to the search results view.
Here is my App.vue component
<template>
<div id="app">
<Navbar></Navbar>
<router-view/>
</div>
</template>
<script>
import Navbar from './components/Navbar.vue'
export default {
name: 'App',
components: {
Navbar
},
}
</script>
And here is my navbar component where I have the input field
<template>
<nav class="navbar">
<h1 class="logo" v-on:click="goToHome">Movie App</h1>
<input class="search-input" v-on:keyup="showResults" v-model="query" type="text" placeholder="Search..."/>
</nav>
</template>
<script>
import router from '../router/index'
export default {
data: function () {
return {
query: this.query
}
},
methods: {
goToHome () {
router.push({name: 'Dashboard'})
},
showResults () {
//here on each key press I want to narrow my results in the SearchedMovies component
}
}
}
</script>
If I use router.push to the SearchedMovies component then I am only able to pass the query as a parameter once. I thought about using Vuex to store the query and then access it from the SearchedMovies component, but surely there is a better way of doing it?
I also read about using $emit but since my parent contains all the routes, I'm not sure how to go about this.
You don't need to redirect user anywhere. I've made a small demo to show how one might do it. I used this navbar component as you described and emit an event from it:
const movies = {
data: [
{
id: 0,
title: 'Eraserhead',
},
{
id: 1,
title: 'Erazerhead',
},
{
id: 2,
title: 'Videodrome',
},
{
id: 3,
title: 'Videobrome',
},
{
id: 4,
title: 'Cube',
},
]
};
Vue.component('navbar', {
template: '<input v-model="filter" #input="onInput" placeholder="search">',
data() {
return {
filter: '',
};
},
methods: {
onInput() {
this.$emit('filter', this.filter);
}
}
});
// this is just a request imitation.
// just waiting for a second until we get a response
// from the datasample
function request(title) {
return new Promise((fulfill) => {
toReturn = movies.data.filter(movie => movie.title.toLowerCase().indexOf(title.toLowerCase()) !== -1)
setTimeout(() => fulfill(toReturn), 1000);
});
}
new Vue({
el: '#app',
data: {
movies: undefined,
loading: false,
filter: '',
lastValue: '',
},
methods: {
filterList(payload) {
// a timeout to prevent
// instant request on every input interaction
this.lastValue = payload;
setTimeout(() => this.makeRequest(), 1000);
},
makeRequest() {
if (this.loading) {
return;
}
this.loading = true;
request(this.lastValue).then((response) => {
this.movies = response;
this.loading = false;
});
}
},
mounted() {
this.makeRequest('');
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<navbar v-on:filter="filterList"></navbar>
<ul v-if="!loading">
<li v-for="movie in movies" :key="movie.id">{{ movie.title }}</li>
</ul>
<p v-else>Loading...</p>
</div>
Also jsfiddle: https://jsfiddle.net/oniondomes/rsyys3rp/
If you have any problem to understand the code above let me know.
EDIT: Fixed some bugs and added a couple of comments
EDIT2(after the comment below):
Here's what you can do. Every time user inputs something inside a navbar you call a function:
// template
<navbar v-on:input-inside-nav-bar="atInputInsideNavBar"></navbar>
// script
methods: {
atInputInsideNavBar(userInput) {
this.$router.push({
path: '/filtred-items',
params: {
value: userInput
}
})
}
}
Then inside you 'searched movies' page component you can access this value so:
this.$route.params.value // returns userInput from root component

Vuejs DOM doesn't update after fetching data

I have bound an array events to a component tag <scheduler> containing events to fill in a scheduler app (dhtmlx scheduler). However, the DOM doesn't seeem to refresh itself when data is retrieved by the getEvents methods triggered when vue instance is created.
There is 2 vue files I work with: App.vue containing the main app component and the Scheduler.vue file containing the scheduler component.
The thing is that when I modify something in the Scheduler.vue file and save it, it correctly take the updated events array into account.
Scheduler parse the data in the events prop when DOM is mounted in scheduler component.
Therefore is there something I can do to get the updated array ?
Here is the App.vue:
<template>
<div id="app">
<div class="container">
<scheduler v-bind:events="events"></scheduler>
</div>
</div>
</template>
<script>
import Scheduler from './components/Scheduler.vue';
import auth from './components/auth/index'
import data from './components/data/index'
export default {
name: 'app',
components: {
Scheduler
},
data() {
return {
events: []
}
},
created() {
this.getEvents();
},
watch: {
events: function(value) {
console.log('updated');
}
},
methods: {
async getEvents() {
try {
const token = await auth.getToken(this);
const thattoken = await auth.getThatToken(this, token);
await data.getOgustData(this, token, '/calendar/events', 307310564, this.events);
} catch (e) {
console.log(e);
}
},
}
}
</script>
Here is Scheduler.vue:
<template lang="html">
<div ref="scheduler_here" class="dhx_cal_container" style='width:100%; height:700px;'>
<div class="dhx_cal_navline">
<div class="dhx_cal_prev_button"> </div>
<div class="dhx_cal_next_button"> </div>
<div class="dhx_cal_today_button"></div>
<div class="dhx_cal_date"></div>
<div class="dhx_cal_tab" name="day_tab" style="right:204px;"></div>
<div class="dhx_cal_tab" name="week_tab" style="right:140px;"></div>
<div class="dhx_cal_tab" name="month_tab" style="right:76px;"></div>
</div>
<div class="dhx_cal_header"></div>
<div class="dhx_cal_data"></div>
</div>
</template>
<script>
import 'dhtmlx-scheduler'
import 'dhtmlx-scheduler/codebase/locale/locale_fr';
import 'dhtmlx-scheduler/codebase/ext/dhtmlxscheduler_readonly.js';
export default {
name: 'scheduler',
props: {
events: {
type: Array,
default () {
return [{
id: '',
text: '',
start_date: '',
end_date: '',
}]
}
}
},
mounted() {
scheduler.config.xml_date = '%Y-%m-%d %H:%i';
// disable left buttons on lightbox
scheduler.config.buttons_left = [];
// enable cancel button on lightbox's right wing
scheduler.config.buttons_right = ['dhx_cancel_btn'];
// changing cancel button label
scheduler.locale.labels['icon_cancel'] = 'Fermer';
// hide lightbox in month view
scheduler.config.readonly_form = true;
// hide select bar in day and week views
scheduler.config.select = false;
scheduler.config.lightbox.sections = [
{
name: "description",
height: 20,
map_to: "text",
type: "textarea",
focus: true
}
];
scheduler.init(this.$refs.scheduler_here, new Date(), 'month');
scheduler.parse(this.$props.events, 'json');
},
}
</script>
<style lang="css" scoped>
#import "~dhtmlx-scheduler/codebase/dhtmlxscheduler.css";
</style>
getOgustData can't populate events in a way that Vue can observe. Since you're passing it as an argument, the array itself can be updated, but it's not a reactive array. Try
var newEvents;
await data.getOgustData(this, token, '/calendar/events', 307310564, newEvents);
this.events = newEvents;
Assigning to this.events is something Vue can notice.
Problem is solved. The issue didn't come from Vue but rather from the dhtmlx scheduler which wasn't parsing events when events was updated.
I ended up watching for any changes to events and thus, parsing it when it updates.
Thanks again for the help provided.
App.vue :
<template>
<div id="app">
<div class="container">
<scheduler v-bind:events="events"></scheduler>
</div>
</div>
</template>
<script>
import Scheduler from './components/Scheduler.vue';
import auth from './components/auth/index'
import data from './components/data/index'
import 'dhtmlx-scheduler'
export default {
name: 'app',
components: {
Scheduler
},
data() {
return {
events: []
}
},
created() {
this.getEvents();
},
watch: {
events: function(value) {
scheduler.parse(this.events, 'json');
}
},
methods: {
async getEvents() {
const token = await auth.getToken(this);
const apiToken = await auth.getApiToken(this, token);
this.events = await data.getApiData(this, apiToken, '/calendar/events', 307310564, this.events);
}
},
}
</script>

Vue component data not updating from props

I'm building a SPA with a scroll navigation being populated with menu items based on section components.
In my Home.vue I'm importing the scrollNav and the sections like this:
<template>
<div class="front-page">
<scroll-nav v-if="scrollNavShown" #select="changeSection" :active-section="activeItem" :items="sections"></scroll-nav>
<fp-sections #loaded="buildNav" :active="activeItem"></fp-sections>
</div>
</template>
<script>
import scrollNav from '.././components/scrollNav.vue'
import fpSections from './fpSections.vue'
export default {
data() {
return {
scrollNavShown: true,
activeItem: 'sectionOne',
scrollPosition: 0,
sections: []
}
},
methods: {
buildNav(sections) {
this.sections = sections;
console.log(this.sections)
},
changeSection(e) {
this.activeItem = e
},
},
components: {
scrollNav,
fpSections
}
}
</script>
this.sections is initially empty, since I'm populating this array with data from the individual sections in fpSections.vue:
<template>
<div class="fp-sections">
<keep-alive>
<transition
#enter="enter"
#leave="leave"
:css="false"
>
<component :is="activeSection"></component>
</transition>
</keep-alive>
</div>
</template>
<script>
import sectionOne from './sections/sectionOne.vue'
import sectionTwo from './sections/sectionTwo.vue'
import sectionThree from './sections/sectionThree.vue'
export default {
components: {
sectionOne,
sectionTwo,
sectionThree
},
props: {
active: String
},
data() {
return {
activeSection: this.active,
sections: []
}
},
mounted() {
this.buildNav();
},
methods: {
buildNav() {
let _components = this.$options.components;
for(let prop in _components) {
if(!_components[prop].hasOwnProperty('data')) continue;
this.sections.push({
title: _components[prop].data().title,
name: _components[prop].data().name
})
}
this.$emit('loaded', this.sections)
},
enter(el) {
twm.to(el, .2, {
autoAlpha : 1
})
},
leave(el, done) {
twm.to(el, .2, {
autoAlpha : 0
})
}
}
}
</script>
The buildNav method loops through the individual components' data and pushes it to a scoped this.sections array which are then emitted back to Home.vue
Back in Home.vue this.sections is populated with the data emitted from fpSections.vue and passed back to it as a prop.
When I inspect with Vue devtools the props are passed down correctly but the data does not update.
What am I missing here? The data should react to props when it is updated in the parent right?
:active="activeItem"
this is calld "dynamic prop" not dynamic data. You set in once "onInit".
For reactivity you can do
computed:{
activeSection(){ return this.active;}
}
or
watch: {
active(){
//do something
}
}
You could use the .sync modifier and then you need to emit the update, see my example on how it would work:
Vue.component('button-counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
props: ['counter'],
watch: {
counter: function(){
this.$emit('update:counter',this.counter)
}
},
})
new Vue({
el: '#counter-sync-example',
data: {
foo: 0,
bar: 0
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="counter-sync-example">
<p>foo {{ foo }} <button-counter :counter="foo"></button-counter> (no sync)</p>
<p>bar {{ bar }} <button-counter :counter.sync="bar"></button-counter> (.sync)</p>
</div>