vue-router reloading App.vue on every new route - vue.js

I have an App.vue file, which has a vue-router component in it that loads the content of my different pages, like this:
<template>
<div>
<router-view
:auth="auth"
:page_id="page_id"
>
</router-view>
</div>
</template>
I also have some AJAX calls in my created() method, which seem to be triggered every time I load a new page via the router. Is there any way to structure things so that my created() method only gets called once when the application loads, not every time a new page within the router loads?

One approach will be using Navigation Guards when you create a logic to determinate when to make your Ajax calls.
Other will be using Vuex, and create a state object to manage the Ajax calls, dispatch an event when the compontent had been loaded before, and bind a computed property to a getter that will contain the value if that component has been loaded previously.

Solution was a lot simpler than I thought:
The App.vue page was using a href links hard-coded to each route.
If you switch to using
<router-link to="path to your route">text here</router-link>
instead, everything works as normal - but Vue doesn't reload the App.vue component!

Related

Vue component communication between header component and components in the router-view

Im facing a problem for my VUE app, Im using the vue Router to navigate to my component
In my Header component I use router-link to navigate to a Home component
The problem is :
In my Header component I would like a checkBox (a boolean variable) that change the content of my Home component (rendered in the router-view) like a v-if that would check the boolean variable in the Header
Here is my App.vue template I was trying to solve the problem through emits but Im Kinda stuck for passing data inside a component (inside the router-view)
<template>
<div class="content">
<HeaderComponent #eventCheckBox="handleCheckBox" />
<router-view />
<FooterComponent />
</div>
Do you guys have already faced this issue, is there a way to do it the classic way or should I try plugins like Portal or Teleport ?
Portal and Teleport are the same, just a different name (teleport in Vue3, being the official name).
As of the rest of the question, this explains it very well: https://stackoverflow.com/a/49702934/8816585
Mainly, you need to see if you need to use a store like Pinia (recommended one) and Vuex. Most of the time, you could use a parent/child relationship. Here is another article explaining when you would need something like that: https://markus.oberlehner.net/blog/should-i-store-this-data-in-vuex/#alternatives-to-storing-data-in-vuex
In Vue3, you could even not use a store and rely solely on the singleton pattern + composables (again, a lot of articles are available for that one).
TLDR: use your Vue devtools and inspect the state flowing.
If you need more, reach for more powerful tools.

Update Vue Web Component attribute from parent onMounted

Because of consuming logic from a separate, server-side templating engine, I find myself working with native Web Components / Custom Elements (built in Vue v3) rather than a more "normal" Vue app.
TL/DR: If I override child CustomElement attributes during parent $onMounted() (or shortly after), it breaks the child's reactivity, but if I wait 1sec everything's fine... Is there a way to get it working properly?
The setup is something like the below, where the HTML is generated at run-time:
<!-- Runtime-generated index.html -->
<outer-custom-element>
<!-- Content populated dynamically by a separate SSR templating engine -->
<inner-custom-element myboolprop otherprop="5" />
<inner-custom-element otherprop="5" />
</outer-custom-element>
Both inner and outer components are Vue-implemented Custom Elements, and the outer component uses a <slot> to transclude the content in its template:
<!-- OuterCustomElement Vue template -->
<div class="outer-el">
<div class="everybody-loves-divs">
<slot></slot>
</div>
</div>
Because the server templating engine controls the inner content, these InnerVueCustomElements are not (and as far as I know cannot be) bound directly to Vue data on the OuterVueCustomElement: If the parent component needs to know what's inside it, we'll have to hack around with DOM and HTMLSlotElement.assignedElements() to interact with the inner components. Yes it's ugly and limiting, but seems pretty unavoidable to me if we have to accept something other than Vue controls the layout of this section of the HTML?
So anyway the problem arises if we try to update inner component props immediately after outer component mount, something like this:
/* OuterCustomElement Vue script */
function $onMounted(props) {
// (Some combination of querySelectorAll and slot assignedElements)
const innerEls: HTMLElement[] = listInnerCustomElements();
// This will permanently break otherprop reactivity on the element:
// innerEls[0].setAttribute("otherprop", "0");
// This will not (works fine):
setTimeout(() => innerEls[0].setAttribute("otherprop", "0"), 1000);
}
setTimeout(..., 10) and Vue's nextTick(...) suffer from the same problem as the immediate call: The inner component will not respond to the change. However if we wait a decent amount of time, this update goes through fine and nothing breaks.
I know this is a long way removed from normal state/render lifecycle management good practice within an all-Vue app, but for a Web Component / Custom Element it should be possible right?
Can anybody suggest what's breaking here and how to avoid it?

When using NuxtLink, how do you perform a store mutation before navigation?

I am trying to update a store value before the router kicks in an navigates to the desired route.
This is my current code which is not working:
<NuxtLink :to="`/posts/${post.slug}`" #click="setPost(post)">
{{ post.title.rendered }}
</NuxtLink>
If I manually trigger the mutation by adding a button like so:
<button #click="setPost(post)">{{ post.title.rendered }}</button>
and then hit the NuxtLink, everything works as expected, but obviously this isn't correct.
How do I ensure that a store mutation is executed before going to the /posts/ page?
Thank you.
A link should be a navigation, not doing something else if following the semantics of HTML.
You can then have some logic when leaving or entering specific pages thanks to Vue router guards: https://router.vuejs.org/guide/advanced/navigation-guards.html
If you want to trigger a vue action (recommended over a vue mutation as stated in the documentation), you can totally call setPost(post) via a button and then, do a $router.push('/posts ....') with your variables as shown in the documentation: https://router.vuejs.org/guide/essentials/navigation.html#router-push-location-oncomplete-onabort

Modal window in nuxt architecture

Before I was working with Vue2JS and I used to creating modal as just component within App.vue root component for example:
<template>
<div>
<app-navbar></app-navbar>
<router-view></router-view>
<app-footer></app-footer>
<my-modal v-if="someBoolean"></my-modal>
</div>
</template>
Now basing on some custom events or Vuex storage I was able to change someBoolean and trigger when I want modal to be visible.
Since in Nuxt we don't have such thing as root App.vue component I'm wondering how to achieve same as above but with Nuxt.
Of course I could use some package as bootstrap-vue but I don't really want to inject this big package just for that one purpose.
You can write code in layouts/default.vue file and this file works on your defaults, work the code at where you used as a layout of your pages(generally almost everywhere.)
Different approach is use portalvue to render components whereever you want. Nice article here but in Turkish.

Vue.js router-view mounted callback

I'm using Vue.js with Vue-router in a project and I'm trying to have a callback whenever the routed-to component is ready. Usually you would do it inside each component in the mounted() hook, but for this case I want it for every component that has been routed to.
I tried with router.OnReady() and router.afterEach() but it did not work since they happen after routing but before the component is mounted. I also tried changing the vue-router source code adding mounted() to the router-view component, but it's not getting called.
There is no on-router event for this but according to this issue you can get around this by using Vue.nextTick inside router.afterEach.