Vuejs 3 Stuck at loading page - vue.js

Hello I have a problem showing/loading page. Some routes are working, and those routes that work are calling API for data, the ones that not work are not calling API for data.
So I am calling API for data(I don't have to, and I don't want to), and then the page loads.
I am watching the selected language computed:{lang(){...}} because in some situations I need to get translated data from API.
Here is the code with added API call.
asyncDataStatus.js
export default{
data(){
return{
asyncDataStatus_ready: false
}
},
methods: {
asyncDataStatus_fetched(){
this.asyncDataStatus_ready = true
this.$emit('ready')
}
},
}
App.vue
<template>
<router-view v-show="showPage" #ready="onPageReady" />
</template>
<script>
export default {
import asyncDataStatus from '#/mixins/asyncDataStatus'
export default {
methods:{
onPageReady(){
this.showPage = true
NProgress.done()
}
},
mixins: [asyncDataStatus],
created(){
NProgress.configure({
speed: 200,
spinner: false
})
this.$router.beforeEach(() => {
this.showPage = false
NProgress.start()
})
}
}
</script>
Contact.vue
<template>
<router-view v-show="showPage" #ready="onPageReady" />
</template>
<script>
import store from '../store';
import asyncDataStatus from '#/mixins/asyncDataStatus'
export default {
mixins: [asyncDataStatus],
computed: {
lang() {
return this.$i18n.locale
},
watch: {
async lang(newLang, oldLang) {
if(newLang !== oldLang) {
// If I remove this line `store.dispatch` line (which is not needed in this file), then `this.asyncDataStatus_fetched()` won't happen
await store.dispatch('impressionModule/getImpressions')
this.asyncDataStatus_fetched()
}
},
created() {
this.asyncDataStatus_fetched()
}
}
}
</script>

Related

how to update component when props changes in nuxt

I want to fetch data everytime when props changes in component and display it without reloading page.
pages/invoice/index.vue:
<template>
<div>
<b-table-column
field="InvoiceNo"
label="Invoice No"
sortable
v-slot="props"
>
<a #click="selectInvoice(props.row.id)">
{{ props.row.invoiceNumber }}
</a>
</b-table-column>
<Invoice :invoiceId="selectedInvoice" />
</div>
</template>
<script>
import axios from "axios";
import Invoice from "../../../components/Invoice.vue";
export default {
components: {
Invoice,
},
data() {
return {
selectedInvoice: "",
}
},
methods: {
selectInvoice(invoiceId) {
this.selectedInvoice = invoiceId;
},
}
}
</script>
components/Invoice.vue:
<script>
import axios from "axios";
export default {
props: ["invoiceId"],
data() {
return {
invoiceData: "",
};
},
watch: {
invoiceId: function (newVal, oldVal) {
this.fetchData(newVal)
},
deep: true,
immediate: true,
},
methods: {
async fetchData(invoiceId) {
let { data: invoiceDetails } = await axios.get(
`${process.env.backendapi}/invoice/byid?invoiceId=${invoiceId}`
);
return {
invoiceData: invoiceDetails,
};
},
},
};
</script>
When I select/change invoice, I can see the backend api getting called everytime with selected invoice, but invoiceData is always blank. The returned result is not getting updated in invoiceData.
I think you want the following in the fetchData method
this.invoiceData = invoiceDetails
Instead of
return {}
Only the already existing data and fetch vue/nuxt functions need to return an object

Vue received a Component which was made a reactive object

The problem I need to solve: I am writing a little vue-app based on VueJS3.
I got a lot of different sidebars and I need to prevent the case that more than one sidebar is open at the very same time.
To archive this I am following this article.
Now I got a problem:
Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with markRaw or using shallowRef instead of ref. (6)
This is my code:
SlideOvers.vue
<template>
<component :is="component" :component="component" v-if="open"/>
</template>
<script>
export default {
name: 'SlideOvers',
computed: {
component() {
return this.$store.state.slideovers.sidebarComponent
},
open () {
return this.$store.state.slideovers.sidebarOpen
},
},
}
</script>
UserSlideOver.vue
<template>
<div>test</div>
</template>
<script>
export default {
name: 'UserSlideOver',
components: {},
computed: {
open () {
return this.$store.state.slideovers.sidebarOpen
},
component () {
return this.$store.state.slideovers.sidebarComponent
}
},
}
</script>
slideovers.js (vuex-store)
import * as types from '../mutation-types'
const state = {
sidebarOpen: false,
sidebarComponent: null
}
const getters = {
sidebarOpen: state => state.sidebarOpen,
sidebarComponent: state => state.sidebarComponent
}
const actions = {
toggleSidebar ({commit, state}, component) {
commit (types.TOGGLE_SIDEBAR)
commit (types.SET_SIDEBAR_COMPONENT, component)
},
closeSidebar ({commit, state}, component) {
commit (types.CLOSE_SIDEBAR)
commit (types.SET_SIDEBAR_COMPONENT, component)
}
}
const mutations = {
[types.TOGGLE_SIDEBAR] (state) {
state.sidebarOpen = !state.sidebarOpen
},
[types.CLOSE_SIDEBAR] (state) {
state.sidebarOpen = false
},
[types.SET_SIDEBAR_COMPONENT] (state, component) {
state.sidebarComponent = component
}
}
export default {
state,
getters,
actions,
mutations
}
App.vue
<template>
<SlideOvers/>
<router-view ref="routerView"/>
</template>
<script>
import SlideOvers from "./SlideOvers";
export default {
name: 'app',
components: {SlideOvers},
};
</script>
And this is how I try to toggle one slideover:
<template>
<router-link
v-slot="{ href, navigate }"
to="/">
<a :href="href"
#click="$store.dispatch ('toggleSidebar', userslideover)">
Test
</a>
</router-link>
</template>
<script>
import {defineAsyncComponent} from "vue";
export default {
components: {
},
data() {
return {
userslideover: defineAsyncComponent(() =>
import('../../UserSlideOver')
),
};
},
};
</script>
Following the recommendation of the warning, use markRaw on the value of usersslideover to resolve the warning:
export default {
data() {
return {
userslideover: markRaw(defineAsyncComponent(() => import('../../UserSlideOver.vue') )),
}
}
}
demo
You can use Object.freeze to get rid of the warning.
If you only use shallowRef f.e., the component will only be mounted once and is not usable in a dynamic component.
<script setup>
import InputField from "src/core/components/InputField.vue";
const inputField = Object.freeze(InputField);
const reactiveComponent = ref(undefined);
setTimeout(function() => {
reactiveComponent.value = inputField;
}, 5000);
setTimeout(function() => {
reactiveComponent.value = undefined;
}, 5000);
setTimeout(function() => {
reactiveComponent.value = inputField;
}, 5000);
</script>
<template>
<component :is="reactiveComponent" />
</template>

Vue Watch not Triggering on Page Refresh/Reload

I have a component which is hid based on the route which is active, it kicks off a function which is stored using vuex store.
It works as intended, the sidenav is hidden on login, logout, and register.
However, I noticed when I am on an authenticated page such as admin panel, or dashboard, etc, the component displays correctly, but when/if someone reloads the webpage, the component disappears, only to be displayed when clicking a link to another page.
App.Vue
<template>
<div id="app">
<navbar />
<sidenav v-show="sidenav_toggle" />
<div class="row router-container">
<div class="col router-row">
<router-view/>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
import Vuex from 'vuex'
import router from '#/router'
import axios from 'axios'
import AxiosStorage from 'axios-storage'
let sessionCache = AxiosStorage.getCache('localStorage');
import materializecss from '../static/css/main.css'
import materializejs from '../static/materialize-css/dist/js/materialize.js'
import navbar from '#/components/navbar'
import sidenav from '#/components/sidenav'
Vue.use(Vuex)
const state = {
sidenav:{
show: false
}
}
const mutations = {
show_sidenav(state){
state.sidenav.show = true
},
hide_sidenav(state){
state.sidenav.show = false
}
}
const store = new Vuex.Store({
state,
mutations
})
export default {
router,
name: 'App',
watch:{
$route: function(){
if(this.$route.path === '/login' || this.$route.path === '/logout' || this.$route.path === '/register'){
store.commit('hide_sidenav')
console.log('not authd')
}else{
store.commit('show_sidenav')
console.log('authd')
}
},
deep: true,
immediate: true
},
computed: {
sidenav_toggle(){
return store.state.sidenav.show
}
},
data: function(){
return{
}
},
components: {
navbar,
sidenav
},
methods: {
},
created: function(){
}
}
</script>
Your watcher is not called if you land directly on the admin page because the $route property never changes (and watchers only watch for changes).
What you could do is move your watcher function in a method, and call this method in the created hook and in your watcher.
An even better way to do this would be to use vue-router navigation-guards
Example:
export default {
// ...
methods: {
adaptSidebar(path) {
if (['/login', '/logout', '/register'].includes(path)) {
store.commit('hide_sidenav')
} else {
store.commit('show_sidenav')
}
},
},
beforeRouterEnter(from, to, next) {
// As stated in the doc, we do not have access to this from here
next(vm => {
vm.adaptSidebar(to.path)
})
},
beforeRouteChange(from, to, next) {
this.adaptSidebar(to.path)
},
}

Make Chartkick wait for data to populate from Firebase before rendering chart

I'm using chartkick in my Vue project. Right now, the data is loading from Firebase after the chart has rendered, so the chart is blank. When I change the code in my editor, the chart renders as expected, since it's already been retrieved from Firebase. Is there a way to make chartkick wait for the data to load before trying to render the chart? Thanks!
Line-Chart Component:
<template>
<div v-if="loaded">
<line-chart :data="chartData"></line-chart>
</div>
</template>
<script>
export default {
name: 'VueChartKick',
props: ['avgStats'],
data () {
return {
loaded: false,
chartData: this.avgStats
}
},
mounted () {
this.loaded = true
}
}
</script>
Parent:
<template>
...
<stats-chart v-if="avgStatsLoaded" v-bind:avgStats="avgStats" class="stat-chart"></stats-chart>
<div v-if="!avgStatsLoaded">Loading...</div>
...
</template>
<script>
import StatsChart from './StatsChart'
export default {
name: 'BBall',
props: ['stats'],
components: {
statsChart: StatsChart
},
data () {
return {
avgStatsLoaded: false,
avgStats: []
}
},
computed: {
sortedStats: function () {
return this.stats.slice().sort((a, b) => new Date(b.date) - new Date(a.date))
}
},
methods: {
getAvgStats: function () {
this.avgStats = this.stats.map(stat => [stat.date, stat.of10])
this.avgStatsLoaded = true
}
},
mounted () {
this.getAvgStats()
}
}
modify your code of StatsChart component:
you may use props directly
<template>
<div v-if="loaded">
<line-chart :data="avgStats"></line-chart>
</div>
</template>
export default {
name: 'VueChartKick',
props: ['avgStats'],
data () {
return {
loaded: false,
}
},
mounted () {
this.loaded = true
}
}

How to pass property to component used in main.js from other components in vuejs

Basically I want to a loadingbar component globally (included in app template)
Here is my loadingbar component
<template>
<div class="loadingbar" v-if="isLoading">
Loading ...
</div>
</template>
<script>
export default {
name: 'loadingbar',
props: ['isLoading'],
data () {
return {
}
}
}
</script>
<style scoped>
</style>
and in main.js, I have included this component as
import LoadingBar from './components/LoadingBar.vue';
new Vue({
router,
data () {
return {
isLoading: true
};
},
methods: {
},
created: function () {
},
components: {
LoadingBar
},
template: `
<div id="app">
<LoadingBar :isLoading="isLoading"/>
<router-view></router-view>
</div>
`
}).$mount('#app');
My aim is to show loading component based upon the value of variable isLoading. The above code working fine. But I want to use set isLoading variable from other component (so that to decide whether to show loading component). Eg. In post components
<template>
<div class="post container">
</div>
</template>
<script>
export default {
name: 'post',
data () {
return {
posts: []
}
},
methods: {
fetchPosts: function() {
// to show loading bar
this.isLoading = true;
this.$http.get(APIURL+'listpost')
.then(function(response) {
// to hide loading bar
this.isLoading = false;
console.log("content loaded");
});
}
},
created: function() {
this.fetchPosts();
}
}
</script>
<style scoped>
</style>
Of coarse we can't access isLoading directly from main.js so i decided to use Mixin so i put following code in main.js
Vue.mixin({
data: function () {
return {
isLoading: false
};
}
});
This however allow me to access isLoading from any other component but I can't modify this variable. Can any help me to achieve this?
Note: I know i can achieve this by including loadingbar in individual component (I tried that and it was working fine, But i do not want to do that as loadingbar is needed in every component so i was including in main template/component)
You could use Vuex like so:
// main.js
import Vuex from 'vuex'
let store = new Vuex.Store({
state: {
isLoading: false,
},
mutations: {
SET_IS_LOADING(state, value) {
state.isLoading = value;
}
},
getters: {
isLoading(state) {
return state.isLoading;
}
}
})
import LoadingBar from './components/LoadingBar.vue';
new Vue({
router,
store, // notice you need to add the `store` var here
components: {
LoadingBar
},
template: `
<div id="app">
<LoadingBar :isLoading="$store.getters.isLoading"/>
<router-view></router-view>
</div>
`
}).$mount('#app');
// script of any child component
methods: {
fetchPosts: function() {
// to show loading bar
this.$store.commit('SET_IS_LOADING', true);
this.$http.get(APIURL+'listpost')
.then(function(response) {
// to hide loading bar
this.$store.commit('SET_IS_LOADING', false);
console.log("content loaded");
});
}
},