Showing a spinner in vue js and bootsrap using v-show - vue.js

I am using vue js 2.5.17 and vue router as front end and laravel as back end. I have a table with over 1000 record so before it shows I want to use a spinner or loader to show the progress. I have managed to use spinner-grow from bootstrap but it keeps showing even when the data is displayed. What I am doing wrong.
In the template I have this:
<div v-show="isloading=true" >
<div class="spinner-grow" role="status">
<span class="sr-only">Loading...</span>
</div>
In data if I have
isloading:true,
In my method i have
loadUser(){
axios.get("api/customer").then((
{data})=>(
this.users=data));
this.isloading=false;
console.log(this.isloading);
},

I think you could try something like this
data() {
return {
isloading: false, // default value
};
},
<div v-show="isloading" >
<div class="spinner-grow" role="status">
<span class="sr-only">Loading...</span>
</div>
loadUser(){
this.isloading=true; // make it true to show the loading
axios.get("api/customer").then((
{data})=>(this.users=data));
this.isloading=false; // then hide it here
console.log(this.isloading);
},

try change this <div v-show="isloading=true" > to this <div v-if="isloading" >

Related

VUE3 Data, props and v-if wont work together?

I have a component called Filter, the parent sends a prop called showFilters. the value of showFilters is ['plaats']. In the Filter component there is a data object called filters. The value of this Object is.
data: function() {
return {
filters: {
plaats: {
title: 'Plaats',
label: 'Plaats_Naam',
key: 'Plaats_Id',
type: 'default',
active: true,
}
}
}
},
in the html part of the Filters component i do this:
<div v-for="key in showFilters" v-if="filters[key].active" class="col py-8" #mouseover="log(filters[key]);">
<h1>test</h1>
</div>
I get a error when doing this because filters[key] is undefined which is weird because when i remove the v-if the log works with the right data.
What am i missing?
Don't use v-if and v-for in the same element, separate v-for in another element like the virtual one template (not the root template) and in the v-if check also filters[key] :
<template v-for="key in showFilters">
<div v-if="filters[key] && filters[key].active" class="col py-8" #mouseover="log(filters[key]);">
<h1>test</h1>
</div>
</template>

Vue/Nuxt Component updates and rerenders without any data changing

I have two components, Carousel.vue and Showcase.vue. I'm testing them both in a page like this:
<template>
<main>
<app-showcase
before-focus-class="app-showcase__element--before-focus"
after-focus-class="app-showcase__element--after-focus"
>
<div class="test-showcase" v-for="n in 10" :key="n">
<img
class="u-image-cover-center"
:src="`https://picsum.photos/1000/1000?random=${n}`"
alt=""
/>
<div>Showcase numero {{ n }}</div>
</div>
</app-showcase>
<div class="u-layout--main u-margin-vertical--4">
<div>
<app-button #click="changeRequestTest(4)">Test Request 4</app-button>
<app-button #click="changeRequestTest(10)">Test Request 10</app-button>
<app-carousel
content-class="u-spread-horizontal--main"
center
:request-element="requestTest"
scroll-by="index"
#index-change="onIndexChange"
>
<template #header>
<h4>Placeholder images</h4>
<div>
Carousel heading h4, showing item number {{ index + 1 }}.
</div>
</template>
<template #default>
<img
:src="`https://picsum.photos/100/80?random=${n}`"
:data-carousel-item-name="n === 10 ? 'giovanni-rana' : ''"
alt=""
v-for="n in 20"
:key="n"
/>
</template>
</app-carousel>
</div>
</div>
</main>
</template>
<script>
export default {
data() {
return {
n1: 20,
n2: 20,
isAnimationOver: false,
index: 0,
requestTest: null,
};
},
methods: {
changeRequestTest(n) {
this.requestTest = n;
},
onIndexChange(e) {
this.requestTest = e;
},
logTest(msg = "hello bro") {
console.log(msg);
},
logElement(e) {
console.log(e);
},
},
created() {
this.requestTest = this.$route.query.hero;
},
};
</script>
Both components use a parameter called index, which basically registers the position (in the children array) of the element that is being focused/shown/highlighted.
...
data() {
return {
index: 0,
showBackButton: true,
showForwardButton: true,
lockScroll: false,
};
},
...
Carousel actually has a prop, requestElement, that allows the carousel to be scrolled to a specific element (via number or element name), and is used in combination with the event "index-change" to update the value in the parent component.
Now that's the problem: every time requestElement updates (there is a watcher in Carousel.vue to follow that), the Showcase component rerenders.
That's a huge problem because the Showcase component applies specific classes on the mounted hook, and on rerender, all that classes are lost. I solved the problem by calling my class-applying-methods in the update hook, but I don't particularly like this performance-wise.
Basically, if I remove any reference to requestElement, everything works as intended, but as soon as that value changes, the showcase rerenders completely. I tried to change props/params names, to use dedicated functions instead of inline scripts, nothing works. No common value is shared, neither via props or vuex, so this makes it even weirder.
Any idea why this happens?
EDIT: I tried the solution from this question, as expected no change was detected in Showcase component, but it still gets updated when requestElement is changed in Carousel.

Is there a way, to "emit" a built in event, by hard coding it in Vue instance?

I would like to link two components with each other in my Vue project.
I use two-way binding for that, so I have a parent, and two child components.
The concept:
We see a carousel in the left side of the screen, and we see an accordion in the right side. I built the carousel and the accordions with v-for from a database file.
When I click in some of the accordion it drops down, and I need a reaction from a carousel component, to slide exactly there, where I clicked in the accordion.
Like:
carousel: banana, apple, house
accordion: banana, apple house
So when im clicking in the apple accordion button, I need the slider to go to the where are the apple is displayed, and reverse.
As I said, I already bind the two components to each other, so when I'm clicking one of the accordion buttons like #click="onShowStart(index)", I get that index in the another child too, and it's changing dynamically vica-versa by sliding or clicking. So the indexes are already linked and its dynamic.
My problem is I don't know how to trigger an event, like #sliding-start from vue instance in the watch field. So I watch the "actualPosition" prop in my component, and when its changed (from 3 to 1 for example), I would like to start a sliding event to the new value of the actualPosition.
So i need something like:
this.$emit('sliding-start', actualPosition);
I've been sitting at this problem for days, but I think my whole thinking is wrong. But before i believe this, im asking you first.
Here is my code for the Parent component:
<div class="row">
<carousel :actualPosition="actualPosition" class="col bg-dark" #sendTheCarouselPosition="updateAccordion($event)"></carousel>
<accordion :actualPosition="actualPosition" class="col bg-dark" #sendTheAccordionlPosition="updateCarousel($event)"></accordion>
</div>
<script>
export default {
data() {
return {
actualPosition: null,
}
},
methods:{
updateAccordion: function (updatedAccordion){
this.actualPosition = updatedAccordion;
},
updateCarousel: function(updatedSlider){
this.actualPosition = updatedSlider
}
},
}
</script>
My Accordion component:
<template>
<div role="tablist">
<b-card no-body class="mb-1" v-for="(item, index) in dataForProject">
<b-card-header header-tag="header" class="p-1" role="tab">
<b-button block href="#" v-b-toggle="'accordion-' + index" variant="info" #click="onShowStart(index)" >{{ item.title }}</b-button>
</b-card-header>
<b-collapse :id="'accordion-' + index" visible accordion="my-accordion" role="tabpanel">
<b-card-body>
<div>
<h1>data from Carousel sibling: {{ actualPosition }}</h1>
</div>
<b-card-text>{{ item.content }}</b-card-text>
</b-card-body>
</b-collapse>
</b-card>
</div>
</template>
<script>
import myDataBase from '../data2'
export default {
props:['actualPosition'],
watch:{
actualPosition: function () {
},
},
data() {
return {
dataForProject: myDataBase,
}
},
methods:{
onShowStart: function (accordionIndex) {
this.$emit('sendTheAccordionlPosition', accordionIndex);
},
},
}
</script>
And my Carousel component:
<template>
<div>
<p class="mt-4 text-white">
data from Accordion sibling: {{ actualPosition }}
</p>
<b-carousel
id="carousel-1"
:interval="0"
controls
indicators
background="#ababab"
img-width="1024"
img-height="480"
style="text-shadow: 1px 1px 2px #333;"
ref="slider"
#sliding-start="onSlideStart"
#sliding-end="onSlideEnd"
>
<b-carousel-slide v-for="(item, index) in dataForProject" :id="index" >
<img
slot="img"
class="d-block img-fluid w-100"
width="1024"
height="480"
:src="item.image_url"
alt="image slot"
>
</b-carousel-slide>
</b-carousel>
</div>
</template>
<script>
import myDataBase from '../data2'
export default {
props:['actualPosition'],
watch: {
actualPosition: function () {
},
},
data() {
return {
//slide: 0,
dataForProject: myDataBase,
}
},
methods: {
onSlideStart(slide) {
this.$emit('sendTheCarouselPosition', slide);
},
onSlideEnd(slide) {
},
}
}
</script>
I can get this done by two ways.
1 - Global EventBus
I will create an eventBus and register events on it from any file and listen it anywhere -
import { EventBus } from '#/eventBus'
// simply import it to component which need listen the event
//Register Event where you have your methods - like In your COMP_B_TWO
EventBus.$on('changeValue', () => { this.doSomething() })
// Emit event from another component
EventBus.$emit('changeValue')// Like directly from your COMP_A_TWO
To know how to create a eventBus follow this - Global Event Bus Vue
2 - Use state management - Vuex Follow this link - Vuex
Basically, it will have centralized store for all the components in an application. Whenever you wish to update state you will update to store. And all the other component using that state will react accordingly
Okay, so my whole concept was wrong, and i was wrong about this.
I should have used the v-model for this whole thing. And nothing more.
I added the v-model to the target tags, and my problem is solved.

Getting ref of component in async v-for

I have a list of items that don't get created until after an async call happens. I need to be able to get the getBoundingClientRect() of the first (or any) of the created items.
Take this code for instance:
<template>
<div v-if="loaded">
<div ref="myItems">
<div v-for="item in items">
<div>{{ item.name }}</div>
</div>
</div>
</div>
<div v-else>
Loading...
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
items: []
}
},
created() {
axios.get('/url/with/some/data.json').then((response) => {
this.items = response.data;
this.loaded = true;
}, (error) => {
console.log('unable to load items');
});
},
mounted() {
// $refs is empty here
console.log(this.$refs);
// this.$refs.myItems is undefined
}
};
</script>
So, I'm trying to access the myItems ref in the mounted() method, but the this.$refs is empty {} at this point. So, therefore, I tried using a component watch, and various other methods to determine when I can read the ref value, but have been unsuccessful.
Anyone able to lead me in the right direction?
As always, thanks again!!
UPDATES
Added a this.$watch in the mounted() method and the $refs still come back as {}. I then added the updated() method to the code, then was able to access $refs there and it seemed to work. But, I don't know if this is the correct solution?
How does vuejs normally handle something like dynamically moving a div to an on-screen position based on async data? This is similar to what I'm trying to do, grab an element on screen once it has been rendered first (if it even should be rendered at all based on the async data), then access it to do something with it (move it to a position)?
Instead of doing on this.$refs.myItems during mounted, you can do it after the axios promise returns the the response.
you also update items and loaded, sou if you want to use watch, you can use those
A little late, maybe it helps someone.
The problem is, you're using v-if, which means the element with ref="myItems" doesn't exist yet. In your code this only happens when Axios resolves i.e. this.loaded.
A better approach would be to use a v-show.
<template>
<div>
<div v-show="loaded">
<div ref="myItems">
<div v-if="loaded">
<div v-for="item in items">
<div>{{ item.name }}</div>
</div>
</div>
</div>
</div>
<div v-show="!loaded">
Loading...
</div>
</div>
</template>
The difference is that an element with v-show will always be rendered and remain in the DOM; v-show only toggles the display CSS property of the element.
https://v2.vuejs.org/v2/guide/conditional.html#v-show

Bootstrap 3 Modal doesnt show with vue 2

I am using vue2 and bootstrap 3 and I want to display a modal containing a form. I have the following code in my UsersPage.vue
<b-btn variant="primary" #click="displayAddUser" >
Add user
</b-btn>
<!-- Modal Component -->
<b-modal id="editModal" :title="editModalTitle" :visible="displayEditModal">
<form #submit.stop.prevent="submit">
<div class="form-group">
<b-form-input type="text" placeholder="Enter name" v-model="user.name"/>
</div>
</form>
</b-modal>
In my <script> section I have this:
<script>
export default {
data: () => ({
displayEditModal: false,
editModalTitle: 'Add user'
}),
methods: {
displayAddUser() {
this.displayEditModal = true
this.editModalTitle = 'Add user'
},
... rest of the code
I have also tried using this button with v-b-modal:
<b-btn v-b-modal.editModal>Open</b-btn>
That didn't work either. I tried vue strap. That worked however when the modal closed I got an error saying something about two-way binding not working any more in vue2. I left react because that was the most impossible piece of software I have ever used and I thought vuejs would be simpler.
How can I get a simple modal or b-modal to show???
Update:
I think I have discovered a bug in vue2 or bootstrap. When I put this in my styling then the modal shows:
.modal-open {
opacity: 0.6 !important;
}
.modal-open .modal {
opacity: 0.9 !important;
}