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

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

Related

Getting the computed method to another components (Vuejs)

i wanted to get the computed data and display in another component. However i put the computed in my app.vue and try to call this computed using :style="inputStyles" in my ChangeBackground.vue . But when i try to do this it showing error that " Property or method "inputStyles" is not defined on the instance but referenced during render" Can someone help me? Thank you
You can access the code here:
https://codesandbox.io/s/hardcore-morning-5ch1u?file=/src/components
Here is the code:
App.vue
<template>
<div id="app">
<ChangeBackground msg="Hello Vue in CodeSandbox!" />
</div>
</template>
<script>
import ChangeBackground from "./components/ChangeBackground";
export default {
name: "App",
components: {
ChangeBackground,
},
data() {
return {
bgColor: "red",
};
},
created() {
this.bgColor = "#F6780D";
},
computed: {
inputStyles() {
return {
background: this.bgColor,
};
},
},
};
</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;
}
body {
background-color: blue;
}
</style>
ChangeBackground.vue
<template>
<div class="hello" :style="inputStyles">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
msg: "Getting the computed area here to change the background",
};
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
h1,
h2 {
font-weight: bold;
}
ul {
list-style-type: none;
padding: 1rem;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
You should pass it as prop as you did with msg :
App.vue
<ChangeBackground msg="Hello Vue in CodeSandbox!" :input-styles="inputStyles" />
ChangeBackground.vue
<template>
<div class="hello" :style="inputStyles">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props:["inputStyles"],//⬅
data() {
return {
msg: "Getting the computed area here to change the background",
};
},
};
</script>

How to clean localeStorage only after closing the browser (VueJs)?

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>

Delete item for todo app in with $emit 2 level up or 1 level up? [duplicate]

This question already has answers here:
Vue 2 - Mutating props vue-warn
(28 answers)
Closed 2 years ago.
I have 3 .vue here: App.vue (default), Todos.vue and Todoitem.vue. I am following the tutorial from https://www.youtube.com/watch?v=Wy9q22isx3U&t=2458. May I know why the author in TodoItem.vue emit id two level up to App.vue to perform the method to delete? Is it best practice or better coding style? Is it easier to just go up one level for Todos.vue to do the same? Below is my one level up approach for any comment.
Below is my TodoItem.vue code
<template>
<div class="todo-item" v-bind:class="{'is-complete':todoObj.completed}">
<p>
<input type="checkbox" v-on:change="markComplete" />
{{todoObj.title}}
<button #click="$emit('del-todo',todoObj.id)" class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todoObj"], // todoObj is defined in the parent.
methods: {
markComplete() {
this.todoObj.completed = !this.todoObj.completed;
}
}
};
</script>
<style scoped>
.todo-item {
background: #f4f4f4;
padding: 10px;
border-bottom: 1px #ccc dotted;
}
.is-complete {
text-decoration: line-through;
}
.del {
background: #ff0000;
color: #fff;
border: none;
padding: 5px 9px;
border-radius: 50%;
cursor: pointer;
float: right;
}
</style>
Below is my Todo.vue code
<template>
<div>
<h1>Todo List2</h1>
<!-- :key= and v-bind:key= are exactly the same. -->
<!-- v-bind. Shorthand: : -->
<div v-for="todo in ptodos" :key="todo.id">
<!-- Define todoObj here which to be used in the child component, TodoItem -->
<MyTodoItem v-bind:todoObj="todo" v-on:del-todo="deleteTodo" />
<!-- del-todo is from the child. child goes up to parent and then to grandparent (App.vue) -->
</div>
</div>
</template>
<script>
import MyTodoItem from "./TodoItem.vue";
export default {
name: "Todos",
components: {
MyTodoItem
},
props: ["ptodos"],
methods: {
deleteTodo(id) {
this.ptodos = this.ptodos.filter(todo => todo.id !== id);
}
}
};
</script>
<style scoped>
</style>
Below is my App.vue code
<template>
<MyToDos v-bind:ptodos="todos" />
</template>
<script>
import MyToDos from "./components/Todos";
export default {
name: "App",
components: { MyToDos },
data() {
return {
todos: [
{
id: 1,
title: "Todo One",
completed: false
},
{
id: 2,
title: "Todo Two",
completed: true
},
{
id: 3,
title: "Todo Three",
completed: false
}
]
};
}
};
</script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
line-height: 1.4;
}
</style>
If you can do it with one level up it's better. To have multiple props on each child can be a bad practice called prop drilling.
Vuex is a good alternative to avoid to get props nested.

In vue js mdb footer get break how to solve this problem?

I am using mdb theme when the project is uploaded on aws instance footer gets break on right side. I checked the css the width of footer is 100% by default for mdb theme.
Below is the code-
App.vue
<template>
<div id="app">
<div class="flexible-content">
<navbar :page="activePage" />
<main class="mt-5 p-5">
<div class="pt-5">
<router-view></router-view>
</div>
</main>
<div class="white-skin">
<copyrights />
</div>
</div>
</div>
</template>
<script>
import SideNav from './components/SideNav'
import Navbar from './components/Navbar'
import Copyrights from './components/Footer'
import * as vm from "vue";
export default {
name: 'App',
mode:'history',
components: {
SideNav,
Navbar,
Copyrights
},
data () {
return {
activePage: 'dashboard',
toggle: false,
loader:true,
}
},
mounted () {
this.activePage = this.$route.name;
this.$on('toggle', function (value) {
this.toggle = value
});
},
updated () {
this.activePage = this.$route.name
}
}
</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>
Footer.vue
<template>
<mdb-footer>
<p class="footer-copyright mb-0 py-3 text-center">
© {{new Date().getFullYear()}} Copyright: xyz
</p>
</mdb-footer>
</template>
<script>
import { mdbFooter } from 'mdbvue'
export default {
name: 'Footer',
components: {
mdbFooter
},
data () {
return {
}
}
}
</script>
<style scoped>
</style>
This is the code of App.vue and Footer.vue .It is perfectly working on localhost but not on instance. After upload on instance footer gets break from right side

Could Vue.js router-view name be changed?

I am new to Vue.js and I know there is more than one component in a route with different names.
In App.vue file, could <router-view name="default"> be changed to other name? Thank you for your help.
HelloWorld.vue
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>Essential Links</h2>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
App.vue
<template>
<div id="app">
<!-- <img src="./assets/logo.png"> -->
<router-view name="default"></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</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>
index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/components/HelloWorld'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
You need dynamic components for this,
Html code
<template>
<div id="app">
<router-view :is="currentComponent"></router-view>
</div>
</template>
Js code: here depending on isTrue value set whichever component you need.
<script>
export default {
name: 'App' ,
components: {
LoginComponent,
HelloComponent
},
computed: {
currentComponent () {
return isTrue ? 'HelloComponent' : 'LoginComponent'
}
}
}
</script>