How to clean localeStorage only after closing the browser (VueJs)? - vue.js

The question was: "How to clean localeStorage only after closing the browser (not closing the browser tab and reloading the page). And this must be implemented in the Vue component.
Found the following solution (Clear localStorage on tab/browser close but not on refresh):
window.onbeforeunload = function (e) {
window.onunload = function () {
window.localStorage.isMySessionActive = "false";
}
return undefined;
};
window.onload = function () {
window.localStorage.isMySessionActive = "true";
};
But I do not understand how to properly and where to call these listeners.

You can simply register your listeners in your main.js file or in your App.vue file
main.js
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
beforeMount() {
window.addEventListener("load", this.onLoad);
window.addEventListener("beforeunload", this.onUnload);
},
beforeDestroy() {
window.removeEventListener("load", this.onLoad);
window.removeEventListener("beforeunload", this.onUnload);
},
methods: {
onLoad(event) {
window.localStorage.setItem("isMySessionActive", true);
},
onUnload(event) {
window.localStorage.setItem("isMySessionActive", false);
}
},
render: (h) => h(App)
}).$mount("#app");
App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" width="25%">
<HelloWorld msg="Hello World!"/>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
components: {
HelloWorld
},
beforeMount() {
window.addEventListener("load", this.onLoad);
window.addEventListener("beforeunload", this.onUnload);
},
beforeDestroy() {
window.removeEventListener("load", this.onLoad);
window.removeEventListener("beforeunload", this.onUnload);
},
methods: {
onLoad(event) {
window.localStorage.setItem("isMySessionActive", true);
},
onUnload(event) {
window.localStorage.setItem("isMySessionActive", false);
}
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

Related

Prevent an addEventListener("click") firing immediately when it is created in vuejs 2

I have a code where I click on a button and it immediately fires the addEventListener("click"), I want to prevent it from firing immediately. the normal thing is that I can see the console.log, until the second click because when I click for the first time I add the listener and after the first time it is already listening to the click events showing console.logs
How can I prevent it?
this is my live code:
<template>
<div id="app">
<button #click="addEvent">add event body</button>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
name: 'App',
methods: {
addEvent() {
document.body.addEventListener('click', this.doSomething);
},
doSomething() {
console.log('click');
},
},
};
https://stackblitz.com/edit/vue-fnxvgg?file=src%2FApp.vue
There's a few solutions you can use ! But I believe stopPropagation() will work the best for you! I also added click.once to your addEvent function as I am assuming you would only want to call it to add the event listener!
<template>
<div id="app">
<button #click.once="addEvent">add event body</button>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
name: 'App',
methods: {
addEvent(event) {
event.stopPropagation()
document.body.addEventListener('click', this.doSomething);
},
doSomething() {
console.log('click');
},
},
};
</script>
Here's the documentation for reference!
https://v2.vuejs.org/v2/guide/events.html#Event-Modifiers
https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation
You need to use the stop modifier to achieve this behavior. It is the same as event.stopPropagation() but it is a vue way of handling events: https://vuejs.org/guide/essentials/event-handling.html#event-modifiers
<template>
<div id="app">
<button #click.stop="addEvent">add event body</button>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
name: 'App',
methods: {
addEvent() {
console.log('ADDED');
document.body.addEventListener('click', this.doSomething);
},
doSomething() {
console.log('click');
},
},
};
</script>
<style>
body {
height: 100vh;
width: 100vw;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Interactive example:
https://stackblitz.com/edit/vue-nknkm9?file=src%2FApp.vue

How to simply navigate from One Component (Home Page ) to Another Component by clicking button using Router concept in Vue.js

I know basics of html, css and js. I have just started learning Vue.js. There is a Home Page in my Vue JS Application which has two buttons. On Click of that button, navigation should happen. (New Component to be loaded). But, in the current code, on button click, navigation is not happening. Please Assist. Copying few file as seen below.
App.vue
<template>
<h3> Home </h3>
<button #click="goToCreate()"> Create Package </button>
<br><br>
<button #click="goToEdit()"> Update Package </button>
</template>
<script>
export default {
name: 'App',
components: {
},
methods:{
goToCreate(){
this.$router.push('/createpackage');
},
goToEdit(){
this.$router.push('/updatepackage');
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
main.js
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
import CreatePackage from './components/CreatePackage.vue'
import SearchAndUpdatePackage from './components/SearchAndUpdatePackage.vue'
const router = createRouter({
history: createWebHistory(),
routes:[
{
path : '/createpackage',
component:CreatePackage
},{
path : '/updatepackage',
component:SearchAndUpdatePackage
}
]
})
const app= createApp(App);
app.use(router).mount('#app')
config.js
let baseUrl = ''
if (process.env.NODE_ENV === 'production') {
baseUrl = 'http://yourdomain.com/api/'
}
else {
baseUrl = 'http://localhost:9000/'
}
export const apiHost = baseUrl
CreatePackage.vue
<template>
<div>
<form name="createPackageForm" #submit="submitNewPackage" method="post">
<input type="number" name="noOfPostpaid" placeholder="PostPaid" v-model="posts.noOfPostpaid">
<br> <br> <br>
<input type="number" name="noOfPrepaid" placeholder="PrePaid" v-model="posts.noOfPrepaid">
<br> <br> <br>
<button>Submit</button>
</form>
</div>
</template>
<script>
import { apiHost } from '../config'
import axios from 'axios'
export default {
name:"CreatePackage",
data(){
return{
posts: {
noOfPostpaid:null,
noOfPrepaid:null
}
}
},
methods:{
submitNewPackage(e){
console.warn(apiHost+'tdg/createpackage/'+this.posts.noOfPostpaid+'/'+this.posts.noOfPrepaid);
e.preventDefault();
axios.post(apiHost+'tdg/createpackage/'+this.posts.noOfPostpaid+'/'+this.posts.noOfPrepaid,{
headers: {
"Access-Control-Allow-Origin": "*"
}},null).then(
response => {
console.log(response.data)}
).catch(e => {
console.log(e);
})
this.posts.noOfPostpaid='';
this.posts.noOfPrepaid='';
}
}
}
</script>
SearchAndUpdatePackage.vue
<template>
<div>
<input type="search" name="accountUUID" placeholder="Account UUID" v-model="posts.accountUUID">
<br> <br> <br>
<button #click="searchAccountUUID">Search </button>
<br> <br> <br>
<textarea id="myTextArea" cols=100 rows=20 v-model="posts.responseJSON"></textarea>
</div>
</template>
<script>
import { apiHost } from '../config'
export default {
name:"SearchAndUpdatePackage",
data(){
return{
posts: {
accountUUID:null,
responseJSON:null
},
}
},
methods:{
searchAccountUUID(e){
const url=apiHost+'tdg/carbon/'+this.posts.accountUUID;
console.log(url);
e.preventDefault();
fetch(url).then(response => response.json())
.then(data=>this.posts.responseJSON=JSON.stringify(data,null,4))
.catch(e => {
console.log(e);
})
console.log(this.posts.responseJSON);
}
}
}
</script>
With Vue-Router, you need to use the router-view component. When you navigate to a URL defined in your routes config, Vue-Router will match that URL and display the associated component.
It's common to place it in App.vue:
<template>
<h3> Home </h3>
<button #click="goToCreate()"> Create Package </button>
<br><br>
<button #click="goToEdit()"> Update Package </button>
<router-view />
</template>
<script>
export default {
name: 'App',
methods: {
goToCreate() {
this.$router.push('/createpackage');
},
goToEdit() {
this.$router.push('/updatepackage');
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
You might have an issue with it being a direct descendent of <template>, but I'm not sure.

How to use vue.js beforeMount lifecycle mehtod?

i am trying to build a weather app in vue js for which i am getting lat and long when windows load and which is working fine. I have defined two variables as lat and long. I have set these variables to the real lat and long i am getting from javascript navigator object.
But when i try to pass these values to the real weather api its not working.
i have some code snippets down below
App.vue
<template>
<div id="app">
<p>{{ lat }},{{ long }}</p>
<pre> {{ forecast }} </pre>
<Search></Search>
<Main></Main>
<ExtraData></ExtraData>
</div>
</template>
<script>
import Search from "./components/Search.vue";
import Main from "./components/Main.vue";
import ExtraData from "./components/ExtraData";
import WeatherService from "./WeatherService";
export default {
name: "app",
components: {
Search,
Main,
ExtraData
},
data() {
return {
lat: "",
long: "",
forecast: "",
test: "",
city: ""
};
},
methods: {
getLocation: function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(pos => {
this.lat = pos.coords.latitude;
this.long = pos.coords.longitude;
});
}
}
},
beforeMount() {
this.getLocation();
},
mounted() {
WeatherService.getCurrentData(this.lat, this.long).then(data => {
this.forecast = data;
});
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
width: 70%;
margin: auto;
padding: 1rem;
}
</style>
WeatherService.js
const apiKey2 = "f02444d5bb635b838a99faefef6eca70";
const api2 = `http://api.weatherstack.com/current?access_key=${apiKey2}&query=`;
//getting current weather data from lat,long
const getCurrentData = async (lat, long) => {
const response = await fetch(api2 + `${lat},${long}`);
const currentLocationData = await response.json();
return currentLocationData;
};
export default { getCurrentData };
Your function getLocation is likely an async function in which case you need to do something like this:
async created(){
await this.getLocation();
WeatherService.getCurrentData(this.lat, this.long).then(data => {
this.forecast = data;
});
you want yout 2 methods in the same lifecycle hook in order to be able to tell one to wait on the other. (In theory this could work in any lifecycle method)

VueJS: background img gets uploaded in all sibling components instead of the component through which image was uploaded

In this codeSandBox demo, the child cmp contains the image file input,
The file is uploaded to and read using reader.readAsDataURL(file) as the background image of the input's parent wrapper div.
The problem is that the uploaded file gets repeated in all siblings.
I want to uploaded file to affect only the component where the child was upload.
Parent.vue
<template>
<div id="app">
<div v-for="n in 5">
<div class="wrapper" :style="bgImg">
<child #imageSelected="updateBgImg($event)"/>
</div>
</div>
</div>
</template>
<script>
import child from "./components/child";
export default {
name: "App",
data() {
return {
bgImgURL: "Image URL",
bgImg: {}
};
},
methods: {
updateBg(url) {
this.bgImgURL = url;
this.bgImg = {
"background-image": "url(" + this.bgImgURL + ")"
}
}
},
components: {
child
}
}
</script>
child.vue
<template>
<div>
<input type="file" #change="getImage">
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String
},
methods: {
getImage(e) {
var file = e.target.files[0];
this.createImage(file);
},
createImage(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
var vm = this;
reader.onload = function() {
vm.$emit("imageSelected", this.result);
};
}
}
};
</script>
Thanks
Update the image in your child. Example on codesandbox
Your parent look like this
<template>
<div id="app">
<div v-for="n in 5" :key='n'>
<child/>
<!-- <child #imageSelected="updateBg($event)"/> -->
</div>
</div>
</template>
<script>
import child from "./components/child";
export default {
name: "App",
data() {
return {
msg: "Parent Message",
// testUrl: "Image URL",
// bgImg: {}
};
},
methods: {
// updateBg(url,index) {
// // this.testUrl = url;
// this.bgImg[index] = {
// "background-image": "url(" + url + ")"
// };
// // console.log(this.bgImg);
// },
},
components: {
child
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.wrapper {
width: 300px;
height: 100px;
margin-bottom: 30px;
border: 1px solid red;
background-size: cover;
}
</style>
and your child like this
<template>
<div>
<div class="wrapper" :style="img">
<input type="file" #change="getImage">
</div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String
},
data(){
return {
img:''
}
},
methods: {
getImage(e) {
var file = e.target.files[0];
this.createImage(file);
},
createImage(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
var vm = this;
reader.onload = function() {
// console.log(this.result)
vm.img = {
"background-image": "url(" + this.result + ")"
}
// don't need
// vm.$emit("imageSelected", this.result);
};
}
}
};
</script>
Updated
Update image in parent from child. Example on codepen
Your parent
<template>
<div id="app">
<div v-for="n in imgCount" :key="n">
<div class="wrapper" :style="imgs[n]">
<sibling/>
<child :image-id="n" #imageSelected="updateBg($event)"/>
</div>
</div>
</div>
</template>
<script>
import child from "./components/child";
import sibling from "./components/simpleCmp";
export default {
name: "App",
data() {
return {
imgCount: 5,
msg: "Parent Message",
testUrl: "Image URL",
bgImg: {},
imgs: []
};
},
methods: {
updateBg(item) {
// console.log(item.index);
this.$set(this.imgs, item.index, item.bg)
}
},
components: {
child,
sibling
}
};
</script>
your child
<template>
<div>
<input type="file" #change="getImage">
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
imageId: Number
},
methods: {
getImage(e) {
var file = e.target.files[0];
if (!file) return;
this.createImage(file);
},
createImage(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
var vm = this;
reader.onload = function() {
// console.log(this.result);
vm.$emit("imageSelected", {
index: vm.imageId,
bg: {
"background-image": "url(" + this.result + ")"
}
});
};
}
}
};
</script>

Error in mounted hook: "RangeError: Maximum call stack size exceeded" VUEJS

I have following router.js in my vuejs app
import Vue from 'vue'
import Router from 'vue-router'
import {client_auth, annotator_auth} from './middleware/auth'
import {reroute_home} from '#/middleware/reroute'
import Editor from './components/editor/Editor.vue'
Vue.use(Router)
const router = new Router({
{
path: '/annotator/studio',
name: 'annotator_studio',
component: Editor,
props: (route) => ({
image_id: route.query.q
}),
meta: {
title: "Annotation Studio",
middleware: annotator_auth
}
}
]
})
function nextFactory(context, middleware, index) {
const subsequentMiddleware = middleware[index];
if (!subsequentMiddleware) return context.next;
return (...parameters) => {
context.next(...parameters);
const nextMiddleware = nextFactory(context, middleware, index + 1);
subsequentMiddleware({ ...context, next: nextMiddleware });
};
}
router.beforeEach((to, from, next) => {
if (to.meta.middleware) {
const middleware = Array.isArray(to.meta.middleware)
? to.meta.middleware
: [to.meta.middleware];
const context = {
from,
next,
router,
to,
};
const nextMiddleware = nextFactory(context, middleware, 1);
return middleware[0]({ ...context, next: nextMiddleware });
}
return next();
});
export default router;
and following is my Editor.uve
<template>
<div id="container">
<div id="view-item">
<app-view/>
</div>
</div>
</template>
<script>
import View from './view/View.vue'
export default {
components: {
'app-view': View,
}
}
</script>
<style scoped>
#container {
display: flex;
flex-flow: column;
height: 100%;
}
#stepper-item {
flex: 0 1 auto;
margin: 5px;
}
#view-item {
display: flex;
flex: 1 1 auto;
margin: 0px 5px 5px 5px;
}
</style>
In this Editor.uve i am importing a child view called View.vue. Following is my View.vue
<template lang="html">
<div
id="view"
class="elevation-2 pointers-please">
<app-osd-canvas/>
<app-annotation-canvas/>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
import OSDCanvas from './OSDCanvas.vue'
import AnnotationCanvas from './AnnotationCanvas.vue'
export default {
components: {
'app-osd-canvas': OSDCanvas,
'app-annotation-canvas': AnnotationCanvas
},
computed: {
...mapState({
projectImageName: state => state.image.projectImageName
})
},
async mounted () {
await this.setProjectImageName('demo')
this.loadDemo()
},
methods: {
...mapActions({
setProjectImageName: 'image/setProjectImageName',
loadDemo: 'editor/loadDemo',
loadProject: 'editor/loadProject'
})
}
}
</script>
<style scoped>
#view {
position: relative;
display: flex;
flex: 1 1 auto;
}
</style>
In this View.vue again i am importing a child component called OSDCanvas.
My OSDCanvas.uve looks like following.
<template lang="html">
<div id="osd-canvas" />
</template>
<script>
import { mapActions } from 'vuex'
export default {
mounted () {
this.setupOsdCanvas('osd-canvas')
},
methods: {
...mapActions({
setupOsdCanvas: 'image/setupOsdCanvas'
})
}
}
</script>
<style>
#osd-canvas {
position: absolute;
height: 100%;
width: 100%;
}
</style>
I am facing following error when i hit /annotator/studio route
[Vue warn]: Error in mounted hook: "RangeError: Maximum call stack size exceeded" found in
---> <AppOsdCanvas> at src/components/editor/view/OSDCanvas.vue
<AppView> at src/components/editor/view/View.vue
<Editor> at src/components/editor/Editor.vue
<VContent>
<VApp>
<App> at src/App.vue
<Root>
I have tried some online solutions as well but the issue is still the same. Any help is appreciated.
UPDATE
following is image/setupOsdCanvas in actions.js in store
setupOsdCanvas: ({
commit
}, payload) => {
commit('setupOsdCanvas', payload)
},
and foloowing is setupOsdCanvas in mutations.js in store
setupOsdCanvas: (state, payload) => {
state.OSDviewer = new openseadragon.Viewer({
id: payload,
showNavigationControl: false,
showNavigator: true,
navigatorId: 'navigator',
maxZoomPixelRatio: 2
})
// Prevent rotation and 'flipping' of the image through the default keybaord
// shortcuts, R and F. (these were conflicting with other annotation tool
// shortcuts when the image was open)
state.OSDviewer.addHandler('canvas-key', e => {
if (e.originalEvent.code === 'KeyR' || e.originalEvent.code === 'KeyF') {
e.preventDefaultAction = true
}
})
}