Composition API problems with start a function when mounted. No data - vue.js

What do I want to achieve:
The form is only loaded if processingData is available.
Currently the form is not loading because processingData is not available
The problem I am currently running into is that when I load the application (in the browser, or possibly f5), no data is visible within processingData. If I modify the application and rebuild again in the editor, I see the correct value. I can't get out anymore..
Create.vue (FILE)
<template>
<FormModal v-if="processingData" :processingData="processingData" #gateway="saveForm" />
</template>
<style lang="scss" scoped>
.el-input--suffix .el-input__inner {
background-color: #f5f8fa;
}
.el-input__inner {
background-color: #f5f8fa;
}
</style>
<script lang="ts">
import { defineComponent, computed, ref, onMounted } from "vue";
import { useStore } from "vuex";
import FormModal from '#/components/forms/FormModal.vue';
export default defineComponent({
name: "",
components: {
FormModal,
},
setup() {
const form = ref({});
const componentName = ref('SalesTable');
const processingData = ref(null)
const store = useStore();
const components1 = computed(() => {
return store.getters.getComponents;
});
function loadComponent() {
console.log(store.getters.getComponents) // Shows nothing (Only after I update the code and rebuild is, it shows data)
console.log(components1.value) // Shows nothing (Only after I update the code and rebuild is, it shows data)
const components = components1.value;
let i;
for (i = 0; i < components.length; i++) {
if (components[i].name == componentName.value) {
processingData.value = components[i];
}
}
}
onMounted(() => {
loadComponent()
})
return {
form,
processingData,
components1,
};
}
});
</script>

Related

How to use $store.commit in Nuxt with #vue/composition-api

<template>
<div>
<h1>Vuex Typescript Test</h1>
<button #click="handleLogin">click</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from '#vue/composition-api'
export default defineComponent({
setup() {
return {
handleLogin() {
// something....
},
}
},
})
</script>
#vue/composition-api do not apply useStore
I want to use store in setup function.
You should be able to access the useStore composable in the setup function according to the documentation of Vuex.
Your script section will look like this:
import { defineComponent } from '#vue/composition-api';
import { useStore } from 'vuex';
export default defineComponent({
setup() {
return {
const store = useStore();
return {
handleLogin {
store.dispatch('auth/login');
},
};
}
},
});
The proper way to structure the content of setup would be to move the handleLogin as a separate const and expose the constant in the return, in order to keep the return section more readable like this:
setup() {
const store = useStore();
const handleLogin = () => {
store.dispatch('auth/login');
};
return {
handleLogin,
}
}

Vue warn]: Property "isMobileTerminal" was accessed during render but is not defined on instance

I'm working on a project that's both mobile and PC,I need to estimate the mobile terminal or PC terminal。
flexible.js
import { computed } from 'vue'
import { PC_DEVICE_WIDTH } from '../constants'
import { useWindowSize } from '#vueuse/core/index'
const { width } = useWindowSize()
// 判断当前是否为移动设备,判断依据屏幕宽度是否小于一个指定宽度(1280)
export const isMobileTerminal = computed(() => {
return width.value < PC_DEVICE_WIDTH
})
and the navigation/index.vue code is
<template>
<mobile-navigation v-if="isMobileTerminal"></mobile-navigation>
</template>
<script>
import { isMobileTerminal } from '../../../../utils/flexible'
import mobileNavigation from './mobile/index.vue'
export default {
name: 'index',
components: {
mobileNavigation
}
}
</script>
<style lang="scss" scoped></style>
My project catalog is shown below
isMobileTerminal is only imported in your component. It also needs to be made available to the template by declaring it in your component definition.
Returning it from the setup() hook is one way to do that:
<script>
import { isMobileTerminal } from '../../../../utils/flexible'
export default {
setup() {
return {
isMobileTerminal
}
}
}
</script>

Toggle div in parent component when button is clicked from child component using Composable

I have 3 pages:
spinner.js - composable. Function to toggle loading spinner
App.vue - parent component
Test.vue - child component
What I need to do is when I click the button from Test.vue, the App.vue should know that the value of loading has been changed and should show/hide the div accordingly.
I tried using watch but I don't really have the total grasp on how to use it. I tried reading the documents but it's still vague for me.
Should I use emit instead for this scenario? But I need to use the composable spinner.js
spinner.js
import { ref } from 'vue'
export default function useSpinner() {
const loading = ref(false)
const isLoading = async (isLoading) => {
loading.value = isLoading
}
return {
loading,
isLoading,
}
}
Test.vue
<template>
<button #click="showSpinner">Show Spinner</button>
</template>
<script>
import useSpinner from "./composables/spinner.js"
export default {
setup() {
const { isLoading } = useSpinner()
// Calls `isLoading()` from `spinner.js` to change the value of `loading`
const showSpinner = async () => {
isLoading(true)
}
return {
loading,
showSpinner,
}
},
}
</script>
App.vue
<template>
<div v-if="loading">Hello Loading Spinner</div>
</template>
<script>
import useSpinner from "./composables/spinner.js"
import { watch } from "vue";
export default {
setup() {
const { loading } = useSpinner()
// should watch when `loading` was changed to toggle div
watch(loading, (currentValue, oldValue) => {
console.log(currentValue);
console.log(oldValue);
});
return {
loading,
}
},
}
</script>
const loading = ref(false) needs to be outside the export.
example:
import { ref } from 'vue';
const loading = ref(false);
export default function useSpinner() {
const isLoading = (isLoading) => {
loading.value = isLoading
}
return {
loading,
isLoading,
};
}
If not, both Test.vue and App.vue will have their own instance of loading when you import the loading ref using the function.

Dynamic layout in Vue 3 with Vite

New to Vue and Vite but trying to get dynamic layouts working properly here. I believe I have what is needed but the issue it the meta seems to always come up as an empty object or undefined.
AppLayout.vue
<script setup lang="ts">
import AppLayoutDefault from './stub/AppLayoutDefault.vue'
import { markRaw, watch } from 'vue'
import { useRoute } from 'vue-router'
const layout = markRaw(AppLayoutDefault)
const route = useRoute()
console.log('Current path: ', route.path)
console.log('Route meta:', route.meta)
watch(
() => route.meta,
async (meta) => {
try {
const component = await import(`./stub/${meta.layout}.vue`)
layout.value = component?.default || AppLayoutDefault
} catch (e) {
layout.value = AppLayoutDefault
}
},
{ immediate: true }
)
</script>
<template>
<component :is="layout"> <router-view /> </component>
</template>
App.vue
<script setup lang="ts">
import AppLayout from '#/layouts/AppLayout.vue'
</script>
<template>
<AppLayout>
<router-view />
</AppLayout>
</template>
Each and every route has the appropriate meta set with a property called layout.
I just can't seem to get he layout applied correctly on the first load or any click of a link in the navbar(which are just router-link) for that matter.
The main problem is layout is initialized as markRaw(), which is not a reactive ref:
const layout = markRaw(AppLayoutDefault) // ❌ not reactive
Solution
Initialize layout as a ref.
Instead of watching the full route object, watch route.meta?.layout because that's the only relevant field for the handler.
Wrap layout's new values with markRaw() to avoid reactivity on the component definition.
const layout = ref() 1️⃣
watch(
() => route.meta?.layout as string | undefined, 2️⃣
async (metaLayout) => {
try {
const component = metaLayout && await import(/* #vite-ignore */ `./${metaLayout}.vue`)
layout.value = markRaw(component?.default || AppLayoutDefault) 3️⃣
} catch (e) {
layout.value = markRaw(AppLayoutDefault) 3️⃣
}
},
{ immediate: true }
)
demo
The solution from Tony is great. But you need to add computed inside the watch because it will prevent code execution for the very first "layout" variable initialized which is still undefined and it will cause unnecessary rendering. So, please add the computed function inside the watch function. Also you need to watch "route.path" not the "route.meta?.layout".
import DefaultLayout from '#/layouts/Default.vue'
import { markRaw, ref } from '#vue/reactivity'
import { computed, watch } from '#vue/runtime-core'
import { useRoute } from 'vue-router'
const layout = ref()
const route = useRoute()
watch(
computed(() => route.path), async () => {
let metaLayout = route.meta?.layout
try {
const metaLayoutComponent = metaLayout && await import(`./layouts/${metaLayout}.vue`)
layout.value = markRaw(metaLayoutComponent?.default || DefaultLayout)
} catch (error) {
layout.value = markRaw(DefaultLayout)
}
}
);

Init pinia state

We use pinia to manage app state. As it's mentioned in title, I'm looking for NuxtServerInit hook analogue for pinia.
A little context: User lands on First page of form; Form calls (f.e.) state.getCountries() to fetch the list of items for one of the select inputs; User selects a country and navigates to Second page, which has to have access to countries list as well; It's ok, when User goes to Second page from the First page; But countries list is empty (obvious) if User refreshes the Second page;
Atm I do like if (state.countries.length === 0) state.getCountries()
But, I believe, it's not a good way
Page 1
<template>
<app-select :items="send.countries" />
</template>
<script>
import { defineComponent } from '#nuxtjs/composition-api'
import { useSend } from '~/store/send'
export default defineComponent({
setup() {
const send = useSend()
send.getCountries()
return { send }
}
}
</script>
Page 2
<template>
<app-select :items="send.countries" />
</template>
<script>
import { defineComponent } from '#nuxtjs/composition-api'
import { useSend } from '~/store/send'
export default defineComponent({
setup() {
const send = useSend()
// if User refreshed Second page, countries is empty list
if (send.countries.length === 0) {
send.getCountries()
}
return { send }
}
}
</script>
store/send.ts
import { defineStore } from 'pinia'
export const useSend = defineStore({
state: () => {
return {
countries: []
}
},
actions: {
getCountries() {
const res = this.$nuxt.$api.countries.get()
this.countries = res.data
}
}
})