NuxtJS - I want to know how to implement Loading only for specific pages - vue.js

I want to display the Nuxt Loading component only on the top page, but it will be displayed on all pages. Can't I display it only when I access a specific page?
Also, the page will be output momentarily before the loading screen starts. Do you know what caused it?
nuxt.config.js
export default {
..
loading: '#/components/Organisms/PageLoading.vue',
..
}
layouts/default.vue
<template>
<div>
<org-page-loading />
</div>
</template>
<script>
import PageLoading from "../components/Organisms/PageLoading";
export default {
name: "Default",
components: {
"org-page-loading": PageLoading,
},
mounted() {
this.$nextTick(() => {
this.$nuxt.$loading.start()
setTimeout(function () {
this.$nuxt.$loading.finish()
}, 2400)
})
},
}
</script>

You can do something like this:
// your-component.vue
<template>
<h1>My Custom page</h1>
</template>
<script>
export default {
loading: false
}
</script>

So firstly the $nextTick probably makes your page show before loading.
Secondly I have never used this kind of loading component, but if you make your own loading component, you can pass a prop to it dynamically, which tells it on which page you want it to be and if the url is not matching, or the component name is not matching it will not show
<your-custom-loading :page="routerLinkYouWantItToBeShownOn" />
EDIT:
I found a better solution:
Heres the spinner/loading component
<template>
<div class="spinner"></div>
</template>
<style lang="css">
.spinner {
position: absolute;
height: 60px;
width: 60px;
border: 3px solid transparent;
border-top-color: var(--v-success-base);
top: 50%;
left: 50%;
margin: -30px;
border-radius: 50%;
animation: spin 2s linear infinite;
z-index: 100;
&:before,
&:after {
content: '';
position: absolute;
border: 3px solid transparent;
border-radius: 50%;
}
&:before {
border-top-color: var(--v-error-base);
top: -12px;
left: -12px;
right: -12px;
bottom: -12px;
animation: spin 3s linear infinite;
}
&:after {
border-top-color: var(--v-accent-lighten1);
top: 6px;
left: 6px;
right: 6px;
bottom: 6px;
animation: spin 4s linear infinite;
}
}
#keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
And here is how u call it:
<template>
<div>
<loading> v-if="isLoading" />
<div v-else>Your other content</div>
</div>
</template>
<script>
import Loading from '#/components/Loading';
export default {
components: {
Loading
},
computed: {
isLoading(){
return "your condition for loading"
}
},
</script>
And you can use a data variable for isloading set to true by default and when u visit the page it will be loading by default
dont even need props

Related

Is it possible to put a CSS template inside a Vue component?

I've created the following Vue component:
var loading = new Vue({
el: "#loading_id",
components:{
'loading' : {
template : `
<div>
<div id="screenLoading" class="background">
<div class="loading">
<div class="obj"></div>
<div class="obj"></div>
<div class="obj"></div>
<div class="obj"></div>
<div class="obj"></div>
<div class="obj"></div>
<div class="obj"></div>
<div class="obj"></div>
</div>
</div>
</div>
`,
methods: {
startLoading: function() {
const load = document.getElementById("screenLoading");
load.classList.add('show')
},
stopLoading: function(){
const load = document.getElementById("screenLoading");
load.classList.remove('show')
}
}
}
}
});
loading.$children[0].startLoading()
setTimeout(() => {
loading.$children[0].stopLoading()
}, 3500);
.background {
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0,.5);
position: fixed;
top: 0px;
left: 0px;
z-index: 2000;
display: none;
justify-content: center;
align-items: center;
}
.background.show {
display: flex;
}
.loading{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 40px;
display: flex;
align-items: center;
}
.obj{
width: 12px;
height: 70px;
background: white;
margin: 0 3px;
border-radius: 10px;
animation: loading 0.8s infinite;
}
.obj:nth-child(2){
animation-delay: 0.1s;
}
.obj:nth-child(3){
animation-delay: 0.2s;
}
.obj:nth-child(4){
animation-delay: 0.3s;
}
.obj:nth-child(5){
animation-delay: 0.4s;
}
.obj:nth-child(6){
animation-delay: 0.5s;
}
.obj:nth-child(7){
animation-delay: 0.6s;
}
.obj:nth-child(8){
animation-delay: 0.7s;
}
#keyframes loading{
0%{
height: 0;
}
50%{
height: 70px;
}
100%{
height: 0;
}
}
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<loading id="loading_id"></loading>
It works perfectly fine, its purpose is to be a loading screen for me to use when I have to make something wait on my webpage. However, instead of loading the CSS from an external CSS file with the <style> tag on the main HTML, I'd like to load my CSS directly from inside Vue, just like I did with the HTML on the template variable. Basically, it'd be something like the following:
var loading = new Vue({
el: "#loading_id",
components:{
'loading' : {
template : `myHTML template`,
cssTemplate: `myCSS template`,
methods: {
// my methods
}
}
}
});
Is it possible to do on Vue? Can I write CSS templates inside a Vue component?
I know I also can load small pieces of CSS styles with Vue as described here, but in my case, I'd like to keep the classes and ids declaration as they are in a normal CSS file...
You can include css in your vue component in the style tag section as below:
<style scoped src="#/assets/styles/myCSSTemplate.css">
</style>
Also try to use proper structure when creating a vue component by having template, script and style sections.
For exmaple you can create Vue instane in App.vue and then have a component as YourComponent.vue
Refer to Vue documentation here.

How to display a component when page is loading in nuxt

I am quite new to nuxt, and I need help here.
async asyncData({ params, route }) {
const { data } = await axios.get(
`${process.env.baseUrl}/homes/?search=${
params.search
}&home_status=${1}`
)
return {
homes: data.results,
}
}
I am trying to populate my component with data(using asyncData), but I want my skeleton loader to show if my page is loading. How do I do that in nuxt?
Here is the code for my skeleton loader;
<template>
<div class="placeholder-container">
<div class="placeholder wave">
<div class="square"></div>
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
</div>
</div>
</template>
<style scoped>
.placeholder-container {
width: 35rem;
margin: 15px auto 15px auto;
}
.placeholder {
padding: 10px;
width: 100%;
// border: 1px solid lightgrey;
display: flex;
flex-direction: column;
}
.placeholder div {
background: #e8e8e8;
}
.placeholder .square {
width: 100%;
height: 22rem;
border-radius: 1rem;
margin: 0 0 10px;
}
.placeholder .line {
height: 12px;
margin: 0 0 10px 0;
}
.placeholder .line:nth-child(2) {
width: 120px;
}
.placeholder .line:nth-child(3) {
width: 180px;
}
.placeholder .line:nth-child(4) {
width: 150px;
}
.placeholder.wave div {
animation: wave 1s infinite linear forwards;
-webkit-animation: wave 1s infinite linear forwards;
background: #f6f7f8;
background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%);
background-size: 800px 104px;
}
#keyframes wave {
0% {
background-position: -468px 0;
}
100% {
background-position: 468px 0;
}
}
#-webkit-keyframes wave {
0% {
background-position: -468px 0;
}
100% {
background-position: 468px 0;
}
}
</style>
What I normally do without using nuxt, is to create a data variable(loading=true), and change it to false after I finish making the api call, but since asyncData runs in the server, how do I make that work? I will also appreciate it if there is a better way of doing something like this
Placeholder
To display a placeholder component on a particular page
during loading, switch from asyncData to the fetch hook, which exposes the $fetchState.pending flag that is set to true when complete:
<template>
<div>
<MyLoading v-if="$fetchState.pending" />
<MyContent v-else :posts="posts" />
</div>
</template>
<script>
export default {
data() {
return {
posts: []
}
},
async fetch() {
const { data } = await this.$axios.get(...)
this.posts = data
}
}
</script>
Customizing loading progress bar
Nuxt provides a default loading progress bar that appears at the top of the app while a page is loading. You could customize the progress bar's appearance:
// nuxt.config.js
export default {
loading: {
color: 'blue',
height: '5px'
}
}
Or you could specify your own custom loading component instead:
// nuxt.config.js
export default {
loading: '~/components/MyLoading.vue'
}
demo

Custom switch component with v-model doesn't work

trying to do my custom Switch component in VueJS following these manuals Using v-model on Components, Adding v-model Support to Custom Vue.js Components but something wrong and it doesn't work.
My SwitchTest component:
<template>
<div>
<input type="checkbox" :id="_uid"
:value="value"
#input="updateData"
class="checkbox">
<label :for="_uid" class="switch"></label>
</div>
</template>
<script>
export default {
props: ["value"],
data() {
return {};
},
methods: {
updateData($event) {
console.log($event.target.value);
this.$emit("input", $event.target.value);
}
}
};
</script>
<style scoped>
.switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
background-color: rgba(0, 0, 0, 0.25);
border-radius: 20px;
transition: all 0.3s;
}
.switch::after {
content: "";
position: absolute;
width: 18px;
height: 18px;
border-radius: 50%;
background-color: white;
top: 1px;
left: 1px;
transition: all 0.3s;
}
.checkbox:checked + .switch::after {
left: 20px;
}
.checkbox:checked + .switch {
background-color: #7983ff;
}
.checkbox {
display: none;
}
</style>
And using SwitchTest component:
{{switch1}}
<SwitchTest v-model="switch1"/>
{{switch2}}
<SwitchTest v-model="switch2"/>
You can see the whole MVCE example here: https://codesandbox.io/s/ecstatic-meninsky-zx7w0?file=/src/App.vue:32-131
When I toggle checkbox, its value doesn't change.
Where am I wrong?
Thank you.
Checkboxes are a special case where they have two states (checked / unchecked) which can be translated to two values. Normally, this is simply true / false but since you've bound a single :value, you'll only ever get the same value each time.
Try binding the Boolean value prop to checked and emit a true / false value
<input
type="checkbox"
:checked="value"
:id="_uid"
#input="$emit('input', $event.target.checked)"
class="checkbox"
>

How to prevent Vue.js hidden element pop-in on page load?

I'm new to Vue.js and I have a (block) element that should be initially hidden on page load. I'm coming from a pure JS mixed with JQuery background so normally I would initially set display:none on the element use JQuery's show/hide methods etc.
I have the showing and hiding working correctly with Vue but a side effect is that the element flashes on the screen briefly on page load until the Vue setup is complete and it knows to hide the element. Setting display:none breaks the show/hide presumably because the elements class prop has higher precedence. Setting opacity:0 also seems to be overriding anything Vue is doing so that breaks the show/hide too. !important on the Vue animation classes does not help either.
The embedded sandbox below might not be the best way to reproduce this, and I suppose it might be system dependent too (speed, memory etc.) but surely this must be a common enough situation with some solution that I've missed.
VUE = new Vue({
el: '#app',
data: {
showFullpageSpinner: false
}
});
setTimeout(function() {
VUE.showFullpageSpinner = true;
setTimeout(function() { VUE.showFullpageSpinner = false; }, 1500);
}, 1500);
.fullpage-spinner-underlay {
position: fixed;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: rgba(0,0,0,0.65);
z-index: 9999;
}
.fullpageSpinner-enter-active, .fullpageSpinner-leave-active {
transition: opacity .25s;
}
.fullpageSpinner-enter, .fullpageSpinner-leave-to {
opacity: 0;
}
.css-spinner {
position: absolute;
display: inline-block;
}
.css-spinner:before {
content: 'Loading...';
position: absolute;
}
.css-spinner:not(:required):before {
content: '';
border-radius: 50%;
border-top: 3px solid #daac35;
border-right: 3px solid transparent;
animation: spinner .7s linear infinite;
-webkit-animation: spinner .7s linear infinite;
}
#keyframes spinner {
to {-ms-transform: rotate(360deg);}
to {transform: rotate(360deg);}
}
#-webkit-keyframes spinner {
to {-webkit-transform: rotate(360deg);}
to {transform: rotate(360deg);}
}
#-moz-keyframes spinner {
to {-moz-transform: rotate(360deg);}
to {transform: rotate(360deg);}
}
.fullpage-loading-spinner {
left: 50%;
top: 45%;
margin-left: -40px;
margin-top: -55px;
}
.fullpage-loading-spinner:BEFORE {
width: 55px;
height: 55px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<transition name="fullpageSpinner">
<div v-if="showFullpageSpinner" class="fullpage-spinner-underlay">
<div class="css-spinner fullpage-loading-spinner"></div>
</div>
</transition>
</div>
Your problem seems to be solvable with the v-cloak directive.
This directive will remain on the element until the associated Vue instance finishes compilation. Combined with CSS rules such as [v-cloak] { display: none }, this directive can be used to hide un-compiled mustache bindings until the Vue instance is ready.
Example:
[v-cloak] {
display: none;
}
<div v-if="showFullpageSpinner" class="fullpage-spinner-underlay" v-cloak>
<div class="css-spinner fullpage-loading-spinner"></div>
</div>

Vuejs carousel simple example not working

I am using Vuejs with Vue-carousel-3d for the carousel. I have the most basic simple carousel component from one of the examples:
<template>
<div id="carousel-wrapper">
<carousel-3d>
<slide v-for="(i, slide) in slides" :index="i" :key="i">
<img src="https://placehold.it/360x270">
</slide>
</carousel-3d>
</div>
</template>
<script>
import { Carousel3d, Slide } from 'vue-carousel-3d';
export default {
name: 'carousel-wrapper',
components: {
'carousel-3d': Carousel3d,
'slide': Slide
},
data: function () {
return {
slides: 7
}
}
}
</script>
<style scoped>
#carousel-wrapper {
outline: 5px solid red;
}
.carousel-3d-container figure {
margin: 0;
}
.carousel-3d-container figcaption {
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
bottom: 0;
padding: 15px;
font-size: 12px;
min-width: 100%;
box-sizing: border-box;
}
</style>
There was an error regarding passing a key in each slide in a for loop, but after correcting it by passing :key="i", now there's no error. But the carousel is not displaying in the browser. If I inspect in developer mode, there is the carousel divs, but in the browser, there's only the 2px solid red outline that I gave for testing purpose. Its working in their example page, but not in mine. I am new to Vuejs so I may be doing something wrong. What am I missing here?