I am a beginner using vue3.
We can use dynamic component like this:
<script setup>
import CommonLayout from "#/components/Layout/CommonLayout.vue";
</script>
<template>
<component :is="CommonLayout>
</component >
</template>
and I try to use dynamic component like this,but it is wrong:
export default {
CommonLayout: () => import("./CommonLayout.vue"),
EmptyLayout: () => import("./EmptyLayout.vue"),
HeaderLayout: () => import("./HeaderLayout.vue"),
};
<script setup>
import layouts from "#/components/Layout/index.js";
const { default: Layout } = await layouts["CommonLayout"]();
</script>
<template>
<Layout>
something
</Layout>
</template>
not error catch but the page show nothing.
and the Layout is the same with CommonLayout:
You need to use defineAsyncComponent
<script setup>
import { defineAsyncComponent } from 'vue'
const CommonLayout = defineAsyncComponent(() => import("./CommonLayout.vue"))
const EmptyLayout = defineAsyncComponent(() => import("./EmptyLayout.vue"))
const HeaderLayout = defineAsyncComponent(() => import("./HeaderLayout.vue"))
</script>
<template>
<component :is="CommonLayout></component>
<component :is="EmptyLayout></component>
<component :is="HeaderLayout></component>
</template>
Related
I am trying to execute a function from the Food.vue component from Page.vue.
How can I execute a function from an imported component?
I am using Vue 3 Composition API.
This is what I am trying to do:
Food.vue Component
<script setup>
var food = "blueberry"
function changeFood () {
food = "cherry";
}
</script>
<template>
<div>{{food}}</div>
</template>
Page.vue
<script setup>
import { onMounted } from "vue";
import food from "#/components/Food.vue";
onMounted(async() => {
// I want to execute changeFood() from the imported component. How can I do this?
});
</script>
<template>
<food></food>
</template>
I know this can be done with page props, but that's not what Im trying to do. I am trying to execute a function.
You have to expose the method to the parent using defineExpose;
Food.vue
<script setup>
import { ref } from "vue";
const food = ref("blueberry");
const changeFood = () => {
food.value = "cherry";
};
defineExpose({ changeFood });
</script>
<template>
<div>{{food}}</div>
</template>
Page.vue
<script setup>
import { ref, onMounted } from "vue";
import food from "#/components/Food.vue";
const myFood = ref(null);
onMounted(async() => {
if (myFood.value) {
myFood.value.changeFood();
}
});
</script>
<template>
<food ref="myFood" />
</template>
Demo
i'm making a project with vue, but the thing i'm stuck on is,
v-bind class type if vertical-navigation-mode-side
Add the open class to the header class, of course, the mobileMode value will also be checked.
same way
If the v-bind class is type vertical-navigation-mode-over, the open class in the header class will be searched and deleted for (responsive) in the first place.
exactly what I want to do is available in the image below
APP.VUE
<template>
<header class="dark" :class="type">Menu</header>
<section class="flex">
<TopHeader/>
<div class="flex">
<RouterView/>
</div>
<Footer/>
</section>
</template>
<script setup>
import TopHeader from "./layouts/TopHeader.vue";
import Footer from "./layouts/Footer.vue";
import {computed, onMounted, onUnmounted, provide, ref} from "vue";
const mobileMode = ref(false)
provide('mobileMode', mobileMode)
function useBreakpoints() {
let windowWidth = ref(window.innerWidth)
const onWidthChange = () => windowWidth.value = window.innerWidth
onMounted(() => window.addEventListener('resize', onWidthChange))
onUnmounted(() => window.removeEventListener('resize', onWidthChange))
const type = computed(() => {
if (windowWidth.value < 550) return 'mode-over'
if (windowWidth.value >= 550 && windowWidth.value < 1200) return 'mode-over'
if (windowWidth.value >= 1200) return 'mode-side'
return null;
})
const width = computed(() => windowWidth.value)
return {width, type}
}
const {width, type} = useBreakpoints()
</script>
TOPHEADER.VUE
<template>
<div class="relative">
<button #click="handleStatus">MODE</button>
</div>
</template>
<script setup>
import {inject} from "vue";
const mobileMode = inject('mobileMode');
const handleStatus = () => mobileMode.value = !mobileMode.value
</script>
FOOTER.VUE
<template>
<div class="relative">
<span class="font-medium text-secondary">FOOTER</span>
</div>
<div class="navigation-overlay" v-if="showMenu===true" #click="handleStatus"></div>
</template>
<script setup>
import {inject} from "vue";
const showMenu = inject('showMenu');
const handleStatus = () => showMenu.value = false
</script>
<style scoped>
</style>
i've got an image loader component, which is getting the image id as prop, normally, i should use the ref like this:
<template>
<img
ref="foo"
/>
</template>
<script setup>
const foo = ref(null);
onMounted( () => {
const img = (foo.value as HTMLImageElement);
if (img) {
img.addEventListener("load", () => fitImage(img));
}
});
</script>
But how do i do it with dynamic ref?
<template>
<img
:ref="dynamicRef()"
/>
</template>
<script setup>
const dynamicRef = () => {
return 'image'+props.imageId;
}
</script>
I've already tried to place this inside an array, like
const refs = [];
refs[dynamicRef] = ref(null)
also
const refs = ref([])
refs.value[dynamicRef] = ref(null);
But nothing seems to work
I have a basic SPA with two child components, a header and a side menu (left drawer).
I wish the user to be able to click a button on the header component to call a function in the side menu component.
I understand I can use props to access a variable between parent & child components however how can I update a value between two sibling components?
Header
<q-btn dense flat round icon="menu" #click="toggleLeftDrawer" />
Left Drawer
import { ref } from 'vue'
export default {
setup () {
const leftDrawerOpen = ref(false)
return {
leftDrawerOpen,
toggleLeftDrawer () {
leftDrawerOpen.value = !leftDrawerOpen.value
}
}
}
}
Use global stores. Create a file
/store.js (you can obviously use any name)
Inside this file store/write the following code:-
import { reactivity } from 'vue'
export const global = reactive({
yourVariable: 'initialValue'
})
You can then import this variable and interact with it from anywhere and the change will be global. See the code below:
In header component:-
<script setup>
import { global } from './store.js'
const clicked = ()=> {
global.yourVariable = 'changed'
}
</script>
<template>
<button #click="clicked">
</button>
</template>
In leftDrawer Component:-
<script setup>
import { global } from './store.js'
</script>
<template>
<div>
{{ global.yourVariable }}
<!--You'll see the change:)-->
</div>
</template>
Then add these two in your main vue:-
<script setup>
import headerComponent from '...'
import leftDrawerComponent from '....'
//....
</script>
<template>
<div>
<header-component />
<left-drawer-component />
</div>
</template>
Goal
How to implement a component that renders an html string (eg fetched from a CMS) passed as a slot like this :
// app.vue
<script setup>
import MyComponent from "./MyComponent.vue"
const htmlStr = `not bold <b>bold</b>`
</script>
<template>
<MyComponent>{{htmlStr}}</MyComponent>
</template>
Explanation
To render an html string (eg fetch from a CMS) we can use v-html :
// app.vue
<script setup>
const htmlStr = `not bold <b>bold</b>`
</script>
<template>
<p v-html="htmlStr"></p>
</template>
Failed attempts
I have tried with no success :
// component.vue
<script>
import { h } from "vue";
export default {
setup(props, { slots }) {
return () =>
h("p", {
innerHTML: slots.default(),
});
},
};
</script>
Renders
[object Object]
Link to playground
Workaround with props
As a workaround, we can of course use props but it's verbose.
// app.vue
<template>
<MyComponent :value="htmlStr">{{htmlStr}}</MyComponent>
</template>
// component.vue
<template>
<p v-html="value"></p>
</template>
<script setup>
import { defineProps } from 'vue'
defineProps(['value'])
</script>
slots.default() returns an array of your passed slot elements, try to map that content and render it :
h("p", {
innerHTML: slots.default().map(el=>el.children).join(''),
});
Playground