I've got a Vue 3 app with Vue Router 4 and I want to achieve this:
<template>
<v-app>
<router-view></router-view> //This is where login would be
<app-layout>
<router-view /> //Everything else
</app-layout>
</v-app>
</template>
<script lang="ts" setup>
import AppLayout from "./components/AppLayout.vue";
</script>
I've got an <app-layout> component which includes navbar and a sidebar, but I don't want to display those when the user is not logged in. I also don't want to wrap each component in <app-layout> individually.
Is there a way I can achieve this?
You can use the Vue-router's v-slot API like this :
<template>
<v-app>
<router-view v-slot="{ Component, route }">
<component v-if="route.name === 'Login'" :is="Component" />
<app-layout v-else>
<component :is="Component" />
</app-layout>
</router-view>
<!-- Or -->
<router-view v-slot="{ Component, route }">
<component :is="route.name === 'Login' ? 'template' : 'app-layout'">
<component :is="Component" />
</component>
</router-view>
</v-app>
</template>
<script lang="ts" setup>
import AppLayout from "./components/AppLayout.vue";
</script>
And in your router, for the login route, don't forget to add the route name :
{
name: 'Login', // important
// ...
}
Related
I have a question about slots. I want to add props / attrs to a default slot like this:
// Component.vue
<template>
<div>Component conten</div>
<slot :required="required" />
</template>
<script lang="ts" setup>
defineProps({
required: {
type: Boolean,
default: false
}
});
</script>
Without having to do this:
// Parent.vue
<template>
<Component required v-slot="{ required }">
<input :required="required" />
</Component>
</template>
So the question is, is there a way to do this without passing props back and forth between component and slot?
I hope i made myself clear. If not, let me know!
Thanks in advance!
I have transitions working between the pages of my vuejs application, defined in App.vue like so:
<template>
<div class="container mb-auto">
<router-view v-slot="{Component}" >
<transition name="slide" mode="out-in">
<component :is="Component" :key="route.path"></component>
</transition>
</router-view>
</div>
<TheFooter v-if="withMenu" />
</template>
// and definition of transitions in css
I don't want this to work between all views (pages) of my app, but only between views who's url starts with /welcome
How do I use some transitions between some pages, and other transitions between other pages?
You can use the JS transitions hooks as shown here: https://vuejs.org/guide/built-ins/transition.html#javascript-hooks
And make a check if you're on the correct path or not, example on a /welcome-home path below
<template>
<div>
<transition #before-enter="onBeforeEnter">
<!-- ... -->
</transition>
</div>
</template>
<script>
export default {
methods: {
onBeforeEnter() {
if (this.$route.path.startsWith('/welcome')) {
// cool transitions!
}
},
},
}
</script>
Is it possible to pass as a prop when calling another component?
Essentially, I have components, and views, I build my views using various components. I want to have 1 styled component which I can reuse, so I was thinking to have a WebsiteLayout.vue:
<template>
<a-layout-content :style="{ padding: '0 24px', minHeight: '280px' }" />
{{ content }}
</template>
<script>
export default {
name: "View",
components: {
},
props: ["content"],
};
</script>
And in my App.vue:
<template>
<Content content=<router-view /> />
</template>
This isnt correct, but wondering if something like this is possible, and how I could achieve it?
With Vue 3 and Router 4, you can do something like
<router-view v-slot="{ Component }">
<SomeComponent>
<component :is="Component" />
</SomeComponent>
</router-view>
and then
//SomeComponent.js
<template>
<div class="wrapper">
<slot></slot>
</div>
</template>
here is an example with transition component as a wrapper for the router-view component
to know more about scoped slots you can see this
I'd like in Vue 3 to be able to provide/overwrite existing components to slots by doing something like:
<ProvideFun>
<p>
Hello
<FunA></FunA>
</p>
</ProvideFun>
<ProvideOtherFun>
<p>
Hello
<FunA></FunA>
</p>
</ProvideOtherFun>
in order to be allowed to use some components (here <FunA>) inside others components (here <ProvideFun> and <ProvideOtherFun>). Note that ProvideFun and ProvideOtherFun may provide different versions of the <FunA> component.
How could I do something like that?
You can find a demo here.
Option 1: Provide/inject components
The parent (i.e., ProvideFun or ProvideOtherFun) could provide its own component definition (named "comp") to be used in FunA:
<!-- ProvideFun.vue -->
<script setup>
import { provide } from 'vue'
import CompA from './CompA.vue'
๐
provide('comp', CompA)
</script>
<template>
<h1>
Provide fun
</h1>
<slot />
</template>
<!-- ProvideOtherFun.vue -->
<script setup>
import { provide } from 'vue'
import CompB from './CompB.vue'
๐
provide('comp', CompB)
</script>
<template>
<h1>
Provide other fun
</h1>
<slot />
</template>
Make FunA.vue a <component> wrapper that injects the "comp" component definition from a parent:
<!-- FunA.vue -->
<script setup>
import { inject } from 'vue'
๐
const comp = inject('comp')
</script>
<template> ๐
<component :is="comp" />
</template>
This requires registering the FunA placeholder component before it could be used in ProvideFun/ProvideOtherFun:
<script setup>
import ProvideFun from './ProvideFun.vue'
import ProvideOtherFun from './ProvideOtherFun.vue'
๐
import FunA from './FunA.vue'
</script>
<template>
<ProvideFun>
<p>
Hello
<FunA />
</p>
</ProvideFun>
<ProvideOtherFun>
<p>
Hello
<FunA />
</p>
</ProvideOtherFun>
</template>
demo 1
Option 2: Components via slot props
Bind the CompA component definition as a slot prop (named "FunA") in ProvideFun.vue, and CompB in ProvideOtherFun.vue:
<!-- ProvideFun.vue -->
<script setup>
import CompA from './CompA.vue'
</script>
<template> ๐
<slot :FunA="CompA">
</template>
<!-- ProvideOtherFun.vue -->
<script setup>
import CompB from './CompB.vue'
</script>
<template> ๐
<slot :FunA="CompB">
</template>
Then use a <component> to render it in the parent:
<script setup>
import ProvideFun from './ProvideFun.vue'
import ProvideOtherFun from './ProvideOtherFun.vue'
</script>
<template>
<ProvideFun> ๐
<template v-slot="{ FunA }">
<p>
Hello ๐
<component :is="FunA" />
</p>
</template>
</ProvideFun>
<ProvideOtherFun> ๐
<template v-slot="{ FunA }">
<p>
Hello ๐
<component :is="FunA" />
</p>
</template>
</ProvideOtherFun>
</template>
demo 2
I probably don't understand how it should be done. I spent a few hours to achieve this functionality but with no luck. Here is what I have:
Child
<template>
<div>
Data from dialog: {{aaa}}
</div>
</template>
<script>
export default {
name: 'frm',
props: [
'aaa'
]
}
</script>
Parent:
<template>
<div>
<slot :aaa="some"></slot>
</div>
</template>
<script>
export default {
name: 'dlg',
data: () => ({
some: 'data from dialog'
})
}
</script>
View:
<template>
<div>
<dlg>
<frm></frm>
</dlg>
</div>
</template>
<script>
import Dialog from '#/components/dialog.vue'
import Frm from '#/components/frm.vue'
export default {
name: "View",
components: {
'dlg': Dialog,
'frm': Frm
}
};
</script>
Edit: Real code
dialog-template:
<template>
<v-dialog
v-model="internal.dialogOpened"
>
<!-- ... -->
<slot :aaa="'dsada'"></slot>
</v-dialog>
</template>
details-task-dialog:
<template>
<dlg-template large position='right' :onclose="close" :load="loadRetry">
<task-details-form /> <!-- just regular component in which I want to get value passed through slot in dialog-template -->
</dlg-template>
</template>
<script>
import DlgTemplate from '#/components/site/dialogs/dialog-template.vue'
export default {
// ...
components: {
'dlg-template': DlgTemplate,
'task-details-form': DetailsForm,
},
I want to avoid passing prop in View but I don't know how :/ I've read about 'slot-scope' unfortunately with no success. How to achieve such functionality?
Edit: real code
Based on your real world code, you were only missing the attachment of the scope, see below.
dialog-template:
<template>
<v-dialog v-model="internal.dialogOpened">
<!-- ... -->
<slot :aaa="'dsada'"></slot>
</v-dialog>
</template>
details-task-dialog:
<template>
<dlg-template large position='right' :onclose="close" :load="loadRetry">
<task-details-form v-slot="{ aaa }">
<!-- you can use the var `aaa` here -->
</task-details-form>
</dlg-template>
</template>
I'd still wager if you want to use aaa inside task-details-form component you have to pass it down as a prop. But it looks wierd to me, because I'm unsure of the execution order right now (v-slot vs v-bind), but try it like this:
<template>
<dlg-template large position='right' :onclose="close" :load="loadRetry">
<task-details-form v-slot="{ aaa }" :your-a-prop-name="aaa" />
</dlg-template>
</template>
Edit 2: after testing
v-bind shorthand is not working on <slot>:
dialog-template:
<template>
<v-dialog v-model="internal.dialogOpened">
<!-- ... -->
<slot v-bind:aaa="'dsada'"></slot>
</v-dialog>
</template>
details-task-dialog:
<template>
<dlg-template large position='right' :onclose="close" :load="loadRetry">
<template v-slot="{ aaa }"> <!-- has to preceed v-bind -->
<task-details-form :propertyOnComponent="aaa" /> <!-- now you cand bind it -->
</template>
</dlg-template>
</template>
Original:
I think you misunderstood slots. Check the docs:
That slot has access to the same instance properties (i.e. the same โscopeโ) as the rest of the template. The slot does not have access to childโs scope.
Slots
So you could do that, but then your code becomes:
<template>
<div>
<dlg>
<frm>
<child :aaa=โdialogDataโ />
</frm>
</dlg>
</div>
</template>
Frm:
<template>
<div>
From component
<slot></slot>
</div>
</template>
If you would define a slot on frm as you are suggesting, you would be able to fill that slot with a specific template and you can receive(!) the scope for that slot.
The deprecated slot-scope which you mentioned would provide you with a bound context from the child to be exposed in your overriding slot in the parent, thatโs the opposite of what you want.
Just out of curiosity, why not send down the dialog data to the form as a property? Thatโs exactly what itโs for.