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>
Related
What is the difference between these two components in Nuxt3 and how do I use them correctly?
If I want to use pages/... what is the right approach here to create links and jump from page to page?
Everything is pretty much explained in the documentation: https://v3.nuxtjs.org/migration/pages-and-layouts/
You need to use this in app.vue
<template>
<nuxt-layout>
<nuxt-page /> <!-- used to display the nested pages -->
</nuxt-layout>
</template>
With a default /layouts/default.vue file
<template>
<div>
this is coming from the layout
<slot /> <!-- required here only -->
</div>
</template>
You will get this on / (with /pages/index.vue)
<template>
<div>index page</div>
</template>
And with the following structure, you will achieve dynamic pages
/pages/users/index.vue
<script setup>
definePageMeta({
layout: false
});
function goToDynamicUser() {
return navigateTo({
name: 'users-id',
params: {
id: 23
}
})
}
</script>
<template>
<div>
<p>
index page
</p>
<button #click="goToDynamicUser">navigate to user 23</button>
</div>
</template>
/pages/users/[id].vue
<script setup>
definePageMeta({
layout: false
});
const route = useRoute()
</script>
<template>
<pre>{{ route.params.id }}</pre>
</template>
I've removed the layout here to show how to disable it, but you can totally let the default here or even provide a custom one.
So, nuxt-page is to be used when you want to display the pages in your app (replacing <nuxt /> and <nuxt-child />) while <slot /> is to be used in the layout (as any other component using the slot tag).
So I'm creating a relatively large website using Vue and Vue-router and different pages require different navigation bars (each custom components rendered on top of the page)(and some pages don't have a navbar at all) and currently I'm using a similar format to this:
<template>
<div>
<div v-if="firstNavbar">
<FirstNav />
<div>
<div v-if="secondNavbar">
<SecondNav />
</div>
<router-view />
</div>
</template>
But re-rendering the entire page for every routing event just because the navbar changed seems a bit inefficient, and I was wondering what I could do to remedy this.
Any help would be appreciated, thanks.
I would suggest the use of <keep-alive> element to cache the navbars. Try this
APP.vue
<template>
<keep-alive>
<component :is="getConditionallyRenderedNavbar"></component>
</keep-alive>
<router-view />
</template>
<script>
import firstNavbar from './firstNavbar.vue';
import secondNavbar from './secondNavbar.vue;
export default{
components:{
firstNavbar,
secondNavbar
},
computed: {
getConditionallyRenderedNavbar() {
return firstNav //or second nav or no nav
}
}
}
</script>
To find out more, check here
I am trying to build a dynamic layout for my application. I have two different layouts, one being DefaultLayout.vue:
<template>
<div>
<main>
<slot/>
</main>
</div>
</template>
and a second one being LayoutWithFooter.vue, with two slots:
<template>
<div>
<main>
<slot/>
</main>
<footer>
<slot name="footer"/>
</footer>
</div>
</template>
My renderless component to handle the dynamic layout looks like this:
<script>
import Vue from 'vue';
import DefaultLayout from './DefaultLayout';
import LayoutWithFooter from './LayoutWithFooter';
export default {
props: {
name: {
type: String,
required: true
}
},
created(){
this.registerComponent("DefaultLayout", DefaultLayout);
this.registerComponent("LayoutWithFooter", LayoutWithFooter);
this.$parent.$emit('update:layout', this.name);
},
methods: {
registerComponent(name, component) {
if(!Vue.options.components[name]) {
Vue.component(name, component);
}
}
},
render() {
return this.$slots.default[0];
},
}
</script>
All of this works fine for the DefaultLayout.vue but when I want to use the LayoutWithFooter.vue, it cannot handle the two slots inside it. Here's an example usage:
<template>
<layout name="LayoutWithFooter">
<div>
<div>some content</div>
<div slot="footer">content for the footer slot</div>
</div>
</layout>
</template>
Problem now is, that the "content for the footer slot" does not get rendered inside of the footer slot of the LayoutWithFooter.vue.
First of all I want you to pay an attention to defining slots level in your example. You provided this code:
<template>
<layout name="LayoutWithFooter">
<div>
<div>some content</div>
<div slot="footer">content for the footer slot</div>
</div>
</layout>
</template>
But actually your div slot="footer" does not refer to footer slot of LayoutWithFooter.vue component. It because of anyway the very first child refers to default slot. And as a result it looks like:
"You want to set content for default slot and inside this default slot you tried to set content for footer slot" - but it's two different scopes.
The right options would look like on the next example:
<template>
<layout name="LayoutWithFooter">
<!-- default slot content -->
<div>
<div>some content</div>
</div>
<!-- footer slot content -->
<div slot="footer">content for the footer slot</div>
</layout>
</template>
I prepared some example based on code and structure you've provided. There you are able to switch layouts and check out how it works and use different components slot in one layout.
Check it here:
https://codesandbox.io/s/sad-fog-zr39m
P.S. Maybe some point are not totally clear, please reply on my answer and I will try to explain more and/or provide you with more links and sources.
I have a component like this:
<test></test>
I declare this as follows:
Vue.component('test', {
data: {
showModal: true
},
methods: {
displayComponentModalDialog: function() {
this.showModal = true;
}
},
template: `<button #click='displayComponentModalDialog'>test</button>`
});
The <test></test> component is then placed somewhere inside the <div id="#app"> wrapper.
var app = new Vue({
router,
el: '#app',
// etc.
})
Now, I want to display another component inside the test component. So in this case I want a dialog to appear after I click the button in test component. I am not able to achieve this.
What I did is adding a new component:
Vue.component('dialog', {
template: '#dialog-template'
});
And then the following code, although I do not know for sure where to put it.
<!-- template for the modal component -->
<script type="text/x-template" id="dialog-template">
<transition name="dialog">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button class="btn btn-primary" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
<!-- use the modal component, pass in the prop -->
<dialog v-if="showModal" #close="showModal = false">
<h3 slot="header">header</h3>
<p slot="body">
test
</p>
</dialog>
I tried putting this code inside the <test></test> but doesn't work. If I put it inside the template attribute in the component structure, it complains about only one root element.
So it is clear I miss some basic conception how this actually works in VueJS. Someone can help me clarify? Thanks.
As far as I can see your component indeed doesn't have a root tag. Templates have to have a root tag.
This is NOT a valid Vue template:
<div>
<h1>Stuff</h1>
</div>
<h2>Other stuff</h2>
This IS a valid Vue template:
<div>
<div>
<h1>Stuff</h1>
</div>
<h2>Other stuff</h2>
</div>
Note that in the second version we have a single root element for the template, a <div>, whereas in the first one we do not.
You have both a <script></script> and a <dialog></dialog> in your component template.
if you want to add another component in your test component . you can use slot on it.
You can refer to this documentation: https://v2.vuejs.org/v2/guide/components-slots.html
Example:
//component 1
<template>
<div id="modal">
// do something for your modal here.
<slot></slot> // reserve area for your another html or component.
</div>
</template>
// component 2
<template>
<your-modal-component>
<another-component>
</your-modal-component>
</template>
I'm new to Vue.js and I'm having a bit of trouble using components with sub-components. I have the following .vue files
app.vue
<template>
<section>
<menu></menu>
<h1>Create Your MIA</h1>
<div id="board"></div>
<slider>
<skin></skin>
</slider>
</section>
</template>
slider.vue
<template>
<div id="slider-panel">
<h3>{{* heading}}</h3>
<div class="slider">
<slot>
Some content
</slot>
</div>
</div>
</template>
<script>
import skin from "./skin";
export default {
components: {
skin: skin
}
};
</script>
skin.vue
<template>
<div v-for="colour in colours">
<div :style="{ backgroundColor: colour }">
<img src="../assets/images/MIA.png"/>
</div>
</div>
</template>
<script>
export default {
data() {
return {
heading: "Choose Skin Tone"
};
}
};
</script>
I'm trying to load the skin sub component into the component. Everything works well except for the skin sub component as it doesn't get compiled. I do not get any compile or vue related errors though. I also wanted to be able to have several instances of the slider component like this
app.vue
<template>
<section>
<menu></menu>
<h1>Create Your MIA</h1>
<div id="board"></div>
<slider>
<skin></skin>
</slider>
<slider>
<foo></foo>
</slider>
<slider>
<bar></bar>
</slider>
</section>
</template>
I'm not sure what I am doing wrong.
I'm not 100% sure of what you want to achieve here, but to compile a component inside a component, you need to add the child component inside the parent's template, like this:
Slider.vue (I've simplified the structure):
<template>
<div id="slider-panel">
<h3>{{* heading}}</h3>
<skin></skin>
</div>
</template>
<script>
import skin from './skin'
export default {
components : {
'skin': skin
}
}
</script>
App.vue:
<template>
<section>
<menu></menu>
<h1>Create Your MIA</h1>
<div id="board"></div>
<slider></slider>
</section>
</template>
Actually, if you add skin in the app's template inside of adding it in the slider component template, it gets included (and rendered) assuming that its scope is app, not slider. In order to add skin inside slider scope, it needs to be added to slider's template. Check this: https://vuejs.org/guide/components.html#Compilation-Scope
Some other things:
Use a hyphen-separated name for the components, with at least 2 words: <custom-slider> instead of <slider>, for example, following the Web Components API (otherwise it might collide with current or upcoming web components).
Slots are complicated to grasp, so read this carefully: https://vuejs.org/guide/components.html#Content-Distribution-with-Slots
Good luck!
Update:
If you want the slider component to be content agnostic and be able to insert anything you want inside it, you have two options (that I can think of):
Remove all the logic from the slider component and make skin a descendant from app. Then use slots in the slider component, as follows:
Slider.vue:
<template>
<div id="slider-panel">
<h3>{{* heading}}</h3>
<slot></slot>
</div>
</template>
<script>
export default {}
</script>
App.vue:
<template>
<section>
<menu></menu>
<h1>Create Your MIA</h1>
<div id="board"></div>
<slider>
<skin></skin>
</slider>
</section>
</template>
<script>
import skin from './skin'
export default {
skin: skin
}
</script>
If you know that the slider will always have a closed set of components inside, you can use dynamic components: https://vuejs.org/guide/components.html#Dynamic-Components
After some research I found this which refers to a is= attribute that will transclude the sub-component template
so in app.vue
<slider-component>
<div is="skin-component" v-for="colour in colours"></div>
</slider-component>
and then add child components