I'm using vue 3 with vue router, and I used the same code from the docs for transitioning the route, and it worked at first, but after some changes in the code in other files, it decided not to work ;-;
Here is my app.vue
<template>
<Nav />
<router-view v-slot="{ Component }">
<transition name="Fade">
<div>
<component :is="Component" />
</div>
</transition>
</router-view>
</template>
<script>
import Nav from "#/components/Nav.vue";
export default {
name: "HomeView",
components: {
Nav,
},
};
</script>
<style scoped>
.Fade-enter-from,
.Fade-leave-to {
opacity: 0;
transform: translateX(120px);
}
.Fade-enter-active,
.Fade-leave-active {
transition: all 0.5s ease-in;
}
</style>
and here's the Main Route
<template>
<div>
<WText />
<Softwares />
</div>
</template>
<script>
import WText from "#/components/WText.vue";
import Softwares from "#/components/Softwares.vue";
export default {
components: {
WText,
Softwares,
},
};
</script>
The other route (contact) is just an empty vue file, nothing special.
transition tag works when inner content re-renders (which could mean Component variable change, setting v-if="true" to v-if="false" and viceversa.), wrapping <component> tag with div will keep a constant dom element existing. Getting rid of wrapper div (or force rerender when Component variable changes which is not a good way to do it.)
Related
I am working on a Vue 3 app. I have run into a problem working with a <Navbar /> component and one of its sub-components.
In App.vue:
<template>
<Navbar />
<Content title="Home" />
<Footer />
</template>
<script>
import Navbar from './components/Navbar.vue'
import Content from './components/Content.vue'
import Footer from './components/Footer.vue'
export default {
name: 'App',
components: {
Navbar,
Content,
Footer
}
}
</script>
Within the Content.vue component, I can display the title this way:
<h1>{{ title }}</h1>
<script>
export default {
name: 'Content',
props: {
title: String
}
}
</script>
But displaying buttons with their labels by the same pattern as above does not work:
// Button.vue component
<template>
<button class="btn btn-sm btn-generate">
{{ label }}
</button>
</template>
<script>
export default {
name: 'Button',
props: {
label: String
}
}
</script>
// Navbar.vue component
<template>
<div class="navbar">
<Button label="Button 1" />
<Button label="Button 2" />
</div>
</template>
<script>
import Button from './Button'
export default {
name: 'Navbar',
components: {
Button
}
}
</script>
The problem
As a result of the code above, inside <div class="navbar"> there is only one button with no label, instead of 2 buttons labeled "Button 1" and "Button 2".
Where is my mistake?
I think that naming your component just as the already existing HTML element is not a good idea. Try changing it to MyButton and use <my-button>... in the navbar component (you still want to use <button> in MyButton, as you want to build upon it).
Most probably browser still picks just a default button instead of yours.
The first essential rule mentioned in Vue docs is Use multi-word component names.
You need to bind them so :label="button 1"
I have a problem of nuxt.js
When I try using components in some pages, and layout is 'something' (layout:'something').
But layout always applied default.
Is this a nuxt bug? or there some rules about it?
// pages/main.vue
<template>
<div>
<something />
</div>
</template>
<script>
import something from "#/components/something.vue";
export default {
components: {
something
},
layout: 'a'
};
</script>
.
.
.
// layouts/a.vue
<template>
<v-app class="dark">
<nuxt/>
</v-app>
</template>
<style scoped>
.dark {
background:black;
}
</style>
.
.
.
// components/something.vue
<template>
<div>
Hello
</div>
</template>
Add some paragraph to your layout and check out if its applied:
<template>
<v-app class="dark">
<p>Hello from layout</p>
<nuxt/>
</v-app>
</template>
Are you not suppose to import the v-app component to your layout and then export it?
<script>
import VApp from "#/components/../..
export default {
components: {
VApp
}
</script>
Of course, depending of the location of your v-app component
I currently have a Vue.js application in which I have used my own component. I want HTML DOM children components to be able know if they are contained in my component or just in the application itself. What would the best way to do it?
I tried it using props, but as it is all contained within another application that does not seem possible.
example: I want the si-button to be able to determine that it is contained in a si-dialog. How do I pass that information to the si-button?
<si-dialog ref="siDialogChildren" name="children">
<div>
<h1>Hello children dialog!</h1>
<p>Click outside the dialog to close this dialog.</p>
</div>
<si-button id="test" type="custom" :on-click="buttonPress">
Click
</si-button>
</si-dialog>
Yours sincerely,
Mirijam
You might want to use $parent as specified in the Vue documentation.
For example, you can get the name of the parent component by accessing to its "$options.name" property.
Example:
App.vue
<template>
<div id="app">
<ParentComponent>
<ChildComponent />
</ParentComponent>
</div>
</template>
<script>
import ParentComponent from './components/ParentComponent'
import ChildComponent from "./components/ChildComponent";
export default {
name: 'App',
components: {
ParentComponent,
ChildComponent
}
}
</script>
<style>
</style>
ParentComponent.vue
<template>
<div>
<p>I'm a parent component.</p>
<slot></slot>
</div>
</template>
<script>
export default {
name: "ParentComponent",
props: {},
computed: {}
};
</script>
<style scoped>
</style>
ChildComponent.vue
<template>
<div>
<p>Parent name: {{componentName}}</p>
<p>I'm inside a ParentComponent? {{componentName === "ParentComponent" ? "Yes!" : "No."}}</p>
</div>
</template>
<script>
export default {
name: "ChildComponent",
computed: {
componentName() {
// We get the parent name from the $options.name property of our parent
// component
return this.$parent.$options.name
}
}
};
</script>
<style scoped>
</style>
NOTE You can also access to other properties on the parent component by using the $parent property. Try it out!
I open the popup in some root component like this:
import parentt from "./parentt.vue";
.
.
.
this.$showModal(parentt, {
fullscreen: true,
});
This is the content of parentt.vue:
<template>
<StackLayout>
<label text="parent" />
<!-- <child /> -->
</StackLayout>
</template>
<script>
import child from "./child.vue";
export default {
components: [child],
};
</script>
<style scoped>
</style>
This is the content of child.vue:
<template>
<StackLayout>
<label text="child" />
</StackLayout>
</template>
<script>
export default {};
</script>
<style scoped></style>
Whith <child /> commented out I get a popup with text parent in it.
with <child /> being there I get a white screen.
I'm using many components in different places in my code, it's only here in a popup that it doesn't work.
You have wrong bracket in components object in parentt.vue. Components is an object, thus use braces instead of the square brackets.
So, the correct script section looks like in parentt.vue:
<script>
import child from "./child.vue";
export default {
components: {
child
},
};
</script>
I recommend for detailed informations the official vue documentation
I am trying to create a splash screen (loading-screen) in Vue JS that after a few seconds fades away, revealing my defaut view. I have tried several approaches but just can't get any to work. The closest is this example on CodePen But ideally the component wouldn't be inside main.js and instead inside its own component. Despite that the below code won't work.
My main.js is as below:
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
Vue.config.productionTip = false;
// FILTERS
Vue.filter('snippet', function(value) {
return value.slice(0,100);
});
Vue.component('loading-screen', {
template: '<div id="loading">Loading...</div>'
})
new Vue({
router,
store,
render: h => h(App),
data: {
isLoading: true
},
mounted () {
setTimeout(() => {
this.isLoading = false
}, 3000)
}
}).$mount("#app");
and my App.vue is as follows
<template>
<div id="app">
<loading-screen v-if="isLoading"></loading-screen>
<Top/>
<router-view/>
<PrimaryAppNav/>
</div>
</template>
<script>
import Top from './components/Top.vue'
import PrimaryAppNav from './components/PrimaryAppNav.vue'
export default {
name: 'app',
components: {
Top,
PrimaryAppNav
}
}
</script>
A LoadingScreen.vue component could look like this:
<template>
<div :class="{ loader: true, fadeout: !isLoading }">
Loading ...
</div>
</template>
<script>
export default {
name: "LoadingScreen",
props: ["isLoading"]
};
</script>
<style>
.loader {
background-color: #63ab97;
bottom: 0;
color: white;
display: block;
font-size: 32px;
left: 0;
overflow: hidden;
padding-top: 10vh;
position: fixed;
right: 0;
text-align: center;
top: 0;
}
.fadeout {
animation: fadeout 2s forwards;
}
#keyframes fadeout {
to {
opacity: 0;
visibility: hidden;
}
}
</style>
Be aware that the loader needs to be aware if loading is done to be able to fade out. You need to check that in your App as well, so it's not showing while it still needs to prepare data. Otherwise, information from the app part in the background could be leaking (for example scrollbars might be visible on the LoadingScreen). So App.vue could have a template like this:
<template>
<div id="app">
<LoadingScreen :isLoading="isLoading" />
<div v-if="!isLoading">
...your main content here...
</div>
</div>
</template>
If you want to have the LoadingScreen divs to disappear completely, you need to manage the state of the fadeout animation in the App.vue itself, making it a bit more complicated (I'd probably use two props for LoadingScreen then: isLoading and fadeout, where fadeout is a callback that gets called in LoadingScreen as soon as the fadeout animation is complete).
I have prepared a codesandbox for you with the state management inside the LoadingScreen.
This is a working App.vue with a splash screen:
<template>
<div id="app">
<v-app :light="!nav.dark" :dark="nav.dark">
<transition name="slide-fade" mode="out-in">
<router-view></router-view>
</transition>
</v-app>
<div v-if="loading" style="position:absolute; width: 100%; height:100%; top:0; left:0; z-index:10000; background-color:white">
<div style="margin-left: auto; margin-right: auto">
Loading...
</div>
</div>
</div>
</template>
<script>
export default {
name: "app",
data: () => ({
loading: true
}),
mounted() {
setTimeout(() => {
this.loading = false
}, 3000)
}
}
</script>
Note that there is a z-index trick and mounted is in App component.
You can of course create a component just for loading, so it will become:
App.vue
<template>
<div id="app">
<v-app :light="!nav.dark" :dark="nav.dark">
<transition name="slide-fade" mode="out-in">
<router-view></router-view>
</transition>
</v-app>
<loader v-if="loading"/>
</div>
</template>
<script>
import Loader from "./Loader"
export default {
name: "app",
data: () => ({
loading: true
}),
mounted() {
setTimeout(() => {
this.loading = false
}, 3000)
}
}
</script>
Loader.vue
<template>
<div style="position:absolute; width: 100%; height:100%; top:0; left:0; z-index:10000; background-color:white">
<div style="margin-left: auto; margin-right: auto">
Loading...
</div>
</div>
</template>
<script>
export default {
name: "loader"
}
</script>
After that, I strongly suggest that you use dynamic components for your router components, Top and PrimaryAppNav. Like that they will load during your splash screen. You will find how to do that very easily in my answer in this thread (only section 2 is relevant for you): here
At beginning show fullscreen splash (with class binding listens for loadedApp).
When vuejs mounted, (or your any other process completed then, change data loadedApp = true.
Then fadeoutHide style will run and hides your splash
<div class="fullscreen-splash" :class="{fadeoutHide:loadedApp}">
// splash logo etc
</div>
data() {
return {
loadedApp: false
}
},
mounted() {
this.loadedApp = true
}
.fadeoutHide {
animation: fadeoutHide .5s forwards;
}
#keyframes fadeoutHide {
to {
opacity: 0;
visibility: hidden;
}
}