Vue 2 Display All Content After Specfic Time - laravel-5.3

Using Vue 2 on Laravel 5.3
I would like to add a loading icon which covers the whole page for 3 seconds allowing all rendering happened, then showing the content after 3 seconds. However, without or with adding the div v-show="!loading" as shown below, the content actually flashed for around half second before it gone disappeared, then after 3 seconds it appears again. So I think there is an unexpected flash at the start when the page is loaded. I read a bit about v-cloak online but it seems only solve short flash of {{}} ?
<template>
<div>
<transition name="fade">
<div v-if="loading" class="loading">Loading…</div>
</transition>
<div v-show="!loading">
<slot>
<!-- Main Content -->
</slot>
</div>
</div>
</template>
<script>
export default {
data(){
return{
loading:true
}
},
props:[
],
mounted(){
var vm = this
vm.loaded()
LoadingEvent.$on('loading', function(loading){
vm.loading = loading
})
},
updated(){
},
methods:{
loaded(){
setTimeout(function(){
LoadingEvent.$emit('loading',false);
}, 1000);
},
}
}
</script>

Try putting both v-if and v-else inside transition like following:
<template>
<div>
<transition name="fade">
<div v-if="loading" class="loading">Loading…</div>
<div v-else>
<slot>
<!-- Main Content -->
</slot>
</div>
</transition>
</div>
</template>

Related

Vue - requests and watch data on unmounted components

I'm new to vue and strugling with some props and attributes.
I have a vue application where the main app is calling three different components:
Navbar,
Sidebar
MapContainer
In Navbar user will fill a selector with information about city and states. After user presses search, the list of results will then show up in the Sidebar menu.
Sidebar itself is a simple component carrying only a router-view for it's children components which are Results and Details
Results will receive the result of the search performed in the Navbar component. When user clicks any item in the Results component, Sidebar will then load Details taking the place of Results with detailed information about that place.
the problem is that the data used to make the request(city and states) comes from the first component navbar. I'm passing this data to sub-components using vue-router params option. When component Results gets unmounted, I lost all the data that was passed, even adding a Watch couldn'd fix the problem and thus can't return back to the previous page. Even adding a Watch couldn'd fix the problem. What's the proper way to handle data across components that area unmounted?
Navbar.vue
<template>
<div class="navbar">
<div class="logo">
Logo
</div>
<div class="middle">
<div class="flex-selectors">
<div class="navbar-options">
<select v-model="state_id" class="main-selectors" #click="load_cities">
<option v-for="state in states" :value="state.state_id" :key="state">
{{ state.state }}
</option>
</select>
<select v-model="city_id" class="main-selectors">
<option v-for="city in cities" :value="city.city_id" :key="city">
{{ city.city }}
</option>
</select>
</div>
<div class="search">
<button #click="seach">
<router-link :to="{name: 'results', params: {state: state_id, city: city_id} }" aria-current="page" title="Resultados">
<div>Search</div>
</router-link>
</button>
</div>
</div>
</div>
</template>
Sidebar.vue
<template>
<div class="sidebar">
<router-view></router-view>
</div>
</template>
Results.vue
<template>
<div v-if="areas.length">
<router-link :to="{name: 'details' }" tag="div" class="container" #click="load(area)" v-for="area in areas" :key="area" :value="area">
<!-- BUNCH OF DIVS AND V-FOR -->
</router-link>
</div>
<div v-else>
NA
</div>
</template>
<script>
export default {
data() {
return {
state_id: this.$route.params.state,
city_id: this.$route.params.city,
areas: [],
}
},
methods: {
search(state_id, city_id) {
load_areas.get(state_id, city_id).then(
result => {
this.areas = result.data
}
)
}
},
mounted() {
this.emitter = inject('emitter')
this.searchAreas(this.state_id, this.city_id)
},
created() {
this.$watch(
() => this.$route.params,
(toParams, previousParams) => {
this.searchAreas(toParams.state, toParams.city)
}
)
},
watch: {}
}
</script>
What's the proper way to handle data across components that are
unmounted?
You cannot access data from an unmounted component.
When multiple components need to access or modify the same data, a good option to look into is state management. Pinia is the new official library recommendation for state management.
Instead of passing data through vue-router params, create a store for it. Set results of your search query from Navbar component and access it in Results component. When Results component gets unmounted, you won't lose the data.

VUE3.JS - Modal in a loop

I have a GET request in my home.vue component.
This query allows me to get an array of objects.
To display all the objects, I do a v-for loop and everything works fine.
<div class="commentaires" v-for="(com, index) of coms" :key="index">
My concern is that I want to display an image by clicking on it (coms[index].imageUrl), in a modal (popup).
The modal is displayed fine but not with the correct image, i.e. the modal displays the last image obtained in the loop, which is not correct.
Here is the full code of my home.vue component
<template>
<div class="container">
<div class="commentaires" v-for="(com, index) of coms" :key="index">
<modale :imageUrl="com.imageUrl_$this.index" :revele="revele" :toggleModale="toggleModale"></modale>
<img class="photo" :src=""" alt="image du commentaire" #click="toggleModale">
</div>
</div>
</template>
<script>
//import axios from "axios";
import axios from "axios";
import Modale from "./Modale";
export default {
name: 'HoMe',
data() {
return {
coms: [],
revele: false
}
},
components: {
modale: Modale
},
methods: {
toggleModale: function () {
this.revele = !this.revele;
},
</script>
Here is my modale.vue component
<template>
<div class="bloc-modale" v-if="revele">
<div class="overlay" #click="toggleModale"></div>
<div class="modale card">
<div v-on:click="toggleModale" class="btn-modale btn btn-danger">X</div>
<img :src=""" alt="image du commentaire" id="modal">
</div>
</div>
</template>
<script>
export default {
name: "Modale",
props: ["revele", "toggleModale", "imageUrl"],
};
</script>
I've been working on it for 1 week but I can't, so thank you very much for your help...
in your v-for loop you're binding the same revele and toggleModale to every modal. When there is only one revele then any time it's true, all modals will be displayed. It's therefore likely you're actually opening all modals and simply seeing the last one in the stack. You should modify coms so that each item has it's own revele, e.g.:
coms = [
{
imageUrl: 'asdf',
revele: false
},
{
imageUrl: 'zxcv',
revele: false
},
{
imageUrl: 'ghjk',
revele: false
}
];
then inside your v-for:
<modale
:image-url="com.imageUrl"
:revele="com.revele"
#toggle-modale="com.revele = false"
></modale>
<img class="photo" :src=""" alt="image du commentaire" #click="com.revele = true">
passing the same function as a prop to each modal to control the value of revele is also a bad idea. Anytime a prop value needs to be modified in a child component, the child should emit an event telling the parent to modify the value. Notice in my code snippet above I replaced the prop with an event handler that turns the revele value specific to that modal to false. Inside each modal you should fire that event:
modale.vue
<div class="btn-modale btn btn-danger" #click="$emit('toggle-modale')">
X
</div>
This way you don't need any function at all to control the display of the modals.

see the menu in all views with vue and css

Hello I am trying to implement my menu that I have created with css in all views, for example in my home view I want to invoke it as I saw an example on the web like this:
<template>
<Fragment>
<menu></menu>
<div class="portada">
<menu></menu>
<div id="desarrollofrontend">
Test Menu
</div>
<div class="text">
<h1>Test Menu Home</h1>
<h3>Description home</h3>
</div>
</div>
</Fragment>
</template>
<script>
import menu from '../components/Head/menu.vue'
export default {
components: { menu }
}
</script>
But so far nothing works for me, I'm really new to this
This is my view of my home page, I have my menu created with scss, in the <menu> </menu> tags it is how I saw some examples on the web but it does not work for me
Any recommendations to make it work?

How in Vue.js could I load images once the route loads without displaying them yet?

I am trying to add a transition of 2 png into an png in the background, and be able to switch between different scenarios ( like living-room, dining-room etc). Once I click the element that select which area of the house I am choosing, the images gets fetched but because it takes some time to load, the transition doesn't happen and they just appear on top of the image on the background without transition smoothly from the side. I need a way to load the all the images once the route gets rendered, or maybe if you could give me another solution for the problem I'm happy to hear it.
This is the template
<template>
<div>
<div v-for="item in painting" :key="item.id" class="showroom">
<div class="setting">
<h1>{{ item.name }}</h1>
<h3>Choose a room:</h3>
<div v-for=" (room, idx) in rooms" :key="idx" id="roomSelection">
<img id="roomSelector" :src="room.roomview" #click="showRoom(room)">
<p>{{ room.name }}</p>
<br>
</div>
<p>Choose a wall color: <span id="colorPicker" :style="{backgroundColor: color}"><input type="color" id="base" v-model="color"></span></p>
<router-link to="/painting"><i class="fas fa-arrow-left"></i></router-link>
</div>
<div class="display">
<div :style="{backgroundColor: color}" class="wall">
<img :src="item.img">
</div>
<div class="forniture">
<transition name="slideIn">
<div v-for=" room in roomToShow" :key="room" class="setForniture">
<img class="left" :id="room.space1" :src="room.forniture1">
<img class="left" :id="room.space2" :src="room.forniture2">
</div>
</transition>
</div>
</div>
</div>
</div>
</template>
This is the script
<script>
import { mapGetters } from 'vuex'
import sofa from '../assets/sofa.png'
import lamp from '../assets/lamp.png'
import table from '../assets/table.png'
import cabinet from '../assets/cabinet.png'
import chair from '../assets/chair.png'
import sidetable from '../assets/sidetable.png'
import livingroom from '../assets/livingroom.png'
import diningroom from '../assets/diningroom.png'
import lounge from '../assets/lounge.png'
export default {
data(){
return{
color: '#243E36',
rooms: [
{ name: "lounge", space1: 'lounge1', space2: 'lounge2', forniture1: chair, forniture2:sidetable, roomview: lounge},
{ name: "livingroom" , space1: 'livingroom1', space2: 'livingroom2', forniture1:sofa, forniture2:lamp, roomview: livingroom },
{ name: "diningroom" , space1: 'diningroom1', space2: 'diningroom2', forniture1: table, forniture2: cabinet, roomview: diningroom }
],
roomToShow: [],
}
},
computed: {
...mapGetters([
'showPainting',
'forSale',
'painting'
]),
},
methods: {
showRoom(room) {
setTimeout( () => {
this.roomToShow.shift();
this.roomToShow.push(room);
}, 100)
}
}
</script>
The easiest way to solve this issue is to just hide the images with CSS but nevertheless load all from the beginning (render the elements). You can do that by dynamically adding a class to the currently visible image with :class="".
A more complex solution, which gives you also a callback for when the images are loaded, is to create a new Image() for all images and react to onload/onerror. Like this, you can load all the images in the beginning with JS abd without changing the HTML. Here is an example for this use case: How to create a JavaScript callback for knowing when an image is loaded?

Ratchet CSS and Marionette Regions

Hello MarionetteJS Gurus,
I have what is probably a very simple problem, but I can't wrap my head around how to solve it. I have an application with 2 regions and 1 layout, and I'm utilizing the Ratchet CSS library for prototyping. Functionally, the goal is simple: display a list of contacts in one view, with a header and search bar. If the user selects a contact, remove the search bar and retain only the header.
The problem appears to be the wrapping divs that are required to swap views in and out. The Ratchet CSS needs the header bars to be directly above the content section in order to properly dock them at the top of the viewport, but Marionette's regions and layouts (at least with my configuration) are preventing this from happening. Perhaps I could better structure my views?
JSFiddle of the resulting output: http://jsfiddle.net/VB7py/3/ You'll notice that the header/search area overlaps the content area.
menuRegion contains a footer menu, and mainRegion contains a layout consisting of a header region, a search region, and a "main content" region. Below are some snippets of my code:
Regions
var App = new Marionette.Application();
App.addRegions({
headerRegion: "#header-region",
menuRegion: "#menu-region",
mainRegion: {
selector: "#main-region",
regionType: MainRegion
}
});
Layouts/Views
List.Layout = Marionette.Layout.extend({
template: "#contacts-layout",
regions: {
headerRegion: "#header-region",
searchRegion: "#secondary-header-region",
contactsRegion: "#contacts-region"
}
});
List.Search = Marionette.ItemView.extend({
tagName: "div",
template: "#contact-list-search",
className: "bar bar-standard bar-header-secondary"
});
List.Header = Marionette.ItemView.extend({
template: "#contact-list-header"
});
Templates
<!-- Application Layout -->
<script type="text/template" id="contacts-layout">
<div id="header-region" class="bar bar-standard"></div>
<div id="secondary-header-region">
</div>
<div id="contacts-region" class="content"></div>
</script>
<script type="text/template" id="contact-list-header">
<nav class="bar bar-standard"><h1 class="title">Contacts</h1></na>
</script>
<script type="text/template" id="contact-list-search">
<input type="search" placeholder="Search" />
</script>
<script type="text/template" id="menu-template">
<nav class="bar bar-tab bar-footer">
</nav>
</script>
The wrapping divs inside the regions did not seem to be a problem. However, you need to set those bar specific css classes to the regions instead of it's nested elements. Like:
<div id="header-region" class="bar bar-standard">
<div>
<nav>
<h1 class="title">All Contacts</h1>
</nav>
</div>
</div>
<div id="secondary-header-region" class="bar bar-header-secondary">
<div>
<input type="search" placeholder="Search">
</div>
</div>
See: http://jsfiddle.net/Cardiff/7Wd6W/
You could pre-set those classes on your region elements or add these specific classes on region initialization, see: https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.region.md#instantiate-your-own-region

Resources