Display a specific route in a different router-view - vue.js

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

Vue 3 - Add props / attrs to slot

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!

vuejs transitions only on some views

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>

can you pass router-view as a prop in vuejs

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

Vuejs 3 : components scope, and how to provide components to slots

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

Passing value from parent to child using slot probably

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.