Conflicting event handlers in vue.js - javascript

I have a page that contains two buttons. I want to use these two buttons to navigate to different pages.
If I just include one of the buttons it works fine. if I include both only one works (as I will show with debug statements if the second button is there, the event handler of the first button is not triggered).
My guess is, that the second button is somehow conflicting with the first button, but I do not know why and how to fix it.
These are some code snippets:
Backbutton.vue
<template>
<div>
<button #click.stop="navigate()"/>
</div>
</template>
<script>
export default {
name: 'BackButton',
methods: {
navigate(){
console.log("B");
}
}
}
</script>
Finishbutton.vue
<template>
<div :style="visible ? { 'display': 'inline-flex' } : { 'display': 'none' }">
<button #click.stop="navigate()"/>
</div>
</template>
<script>
export default {
name: 'FinishButton',
props : {
visible: Boolean
},
methods: {
navigate(){
console.log("F");
}
}
}
</script>
Page.vue
<template>
<BackButton/>
<FinishButton :visible=ready></FinishButton>
</template>
<script>
import BackButton from "../components/BackButton.vue"
import FinishButton from "../components/FinishButton.vue"
export default {
name: 'Page',
components: {
BackButton,
FinishButton
},
data() {
return {
ready: true
}
},
}
</script>
If ready on the page is false (and therefore the finish-button is not visible), a click on the backbutton prints "B". If ready is true, the finishbutton prints "F", but clicking on the backbutton does not yield any output.
Help is greatly appreciated.

There are some minor issues here and there with your code, but the following works well (not sure where exactly this is coming from tho).
Page.vue
<template>
<div>
<BackButton></BackButton>
<FinishButton :visible="ready"></FinishButton>
</div>
</template>
<script>
import BackButton from '../components/BackButton.vue'
import FinishButton from '../components/FinishButton.vue'
export default {
name: 'Page',
components: {
BackButton,
FinishButton,
},
data() {
return {
ready: true,
}
},
}
</script>
BackButton.vue
<template>
<div>
<button #click.stop="navigate">back</button>
</div>
</template>
<script>
export default {
name: 'BackButton',
methods: {
navigate() {
console.log('B')
},
},
}
</script>
FinishButton.vue
<template>
<div :style="visible ? { display: 'inline-flex' } : { display: 'none' }">
<button #click.stop="navigate">finish</button>
</div>
</template>
<script>
export default {
name: 'FinishButton',
props: {
visible: Boolean,
},
methods: {
navigate() {
console.log('F')
},
},
}
</script>
Or at least, I cannot reproduce your issue with the provided snippet.

Related

Active events in vue instance encapsulated inside shadowDOM

I'm searching to trigger event from a vue instance which is encapsulated inside a shadowDOM.
For the moment, my code create a new Vue instance inside a shadowDOM and print the template without the style (because nuxt load the style inside the base vue instance).
I can call methods of each instance
But, when i'm trying to add an event / listener on my component, it doesn't work.
DOM.vue
<template>
<section ref='shadow'></section>
</template>
<script>
import bloc from '#/components/bloc.vue'
import Vue from 'vue'
export default {
data() {
return {
vm: {},
shadowRoot: {},
isShadowReady: false
}
},
watch: {
isShadowReady: function() {
let self = this
this.vm = new Vue({
el: self.shadowRoot,
components: { bloc },
template: "<bloc #click='listenClick'/>",
methods: {
listenClick() { console.log("I clicked !") },
callChild() { console.log("I'm the child !") }
},
created() {
this.callChild()
self.callParent()
}
})
}
},
mounted() {
const shadowDOM = this.$refs.shadow.attachShadow({ mode: 'open' })
this.shadowRoot = document.createElement('main')
shadowDOM.appendChild(this.shadowRoot)
this.isShadowReady = true
},
methods: {
callParent() {
console.log("I'am the parent")
},
}
}
</script>
bloc.vue
<template>
<div>
<p v-for='word in words' style='text-align: center; background: green;'>
{{ word }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
words: [1,2,3,4,5,6]
}
}
}
</script>
Any idea ?
Thank you
Hi so after i watched at your code i think it's better to use the $emit to pass event from the child to the parent and i think it's more optimized then a #click.native
template: "<bloc #someevent='listenClick'/>",
<template>
<div #click="listenClickChild">
<p v-for='(word, idx) in words' :key="idx" style='text-align: center; background: green;'>
{{ word }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
words: [1,2,3,4,5,6]
}
},
methods: {
listenClickChild() {
this.$emit('someevent', 'someValue')
}
}
}
</script>
bloc #click.native='listenClick'/>

CSS of component balise doesn't load inside spefic component

I've a problem to load the css of my bloc component.
The webpage component allow to create an iframe and set some content inside easily.
It load correctly the template and script tag but not the css (it doesn't load).
Sometime it works, most of the time, it didn't.
I was thinking that it was a problem with the loading of the component but no.
If I load the component before or after the render of my "webpage" component : it don't load.
I've try with the auto import to true and after to false, but it solve nothing.
I have 2 components : webpage and bloc.
bloc.vue
<template>
<div class="bloc">
<p>Le texte</p>
</div>
</template>
<style>
.bloc {
background-color: blue;
padding: 20px;
}
</style>
webpage.vue
<script>
import Vue from "vue";
export default {
props: {
css: {
type: String,
required: false,
},
},
data() {
return {
load: false,
};
},
render(h) {
return h("iframe", {
on: { load: this.renderChildren },
});
},
beforeUpdate() {
//freezing to prevent unnessessary Reactifiation of vNodes
this.iApp.children = Object.freeze(this.$slots.default);
},
mounted() {
if (!this.load) this.renderChildren();
},
methods: {
// https://forum.vuejs.org/t/render-inside-iframe/6419/12
renderChildren() {
this.load = true;
const children = this.$slots.default;
const head = this.$el.contentDocument.head;
const body = this.$el.contentDocument.body;
let style = this.$el.contentDocument.createElement("style");
style.textContent += this.$props.css;
head.appendChild(style);
const iApp = new Vue({
name: "iApp",
// freezing to prevent unnessessary Reactifiation of vNodes
data: { children: Object.freeze(children) },
render(h) {
return h("body", this.children);
},
});
this.iApp = iApp; // cache instance for later updates
this.iApp.$mount(body); // mount into iframe
},
},
};
</script>
app.vue
<template>
<Webpage>
<component :is="name"></component>
</Webpage>
</template>
<script>
import bloc from "#/components/Bloc";
import Webpage from "#/components/Webpage";
export default {
components: {
bloc,
Webpage,
},
computed: {
name() {
return "bloc";
},
},
};
</script>
Do you have an idea where this might come from ?
The codesanbox : https://codesandbox.io/s/error-style-component-import-1t1hs?file=/pages/index.vue
Thank you.
Probably Webpage component overrides it.
Try moving your style to index and <Webpage class="bloc">

Default click on a button when component loads in vue js

I have a button in vue component within template as follow:
<a href="#" #click="openTab" class="border-red px-8" id="activeSlide" data-target-quote="#yvoefrance">
<img :src="inactive_logo[0]" class="logo" alt="yvoefrance logo" />
</a>
I want it to be clicked by default when components loads after refreshing the page, how can I achieve this? I tried following but didn't work for me.
I thought the right place is created. Can anyone help? Thank you in advance.
export default {
name: "component.showcase",
components: {
// ...
},
data() {
return {
// data here....
};
},
created() {
document.querySelector("#activeSlide").click();
},
mounted() {},
beforeDestroy() {},
computed: {},
methods: {
openTab: function(e) {
e.preventDefault();
const target_tab = e.target.parentElement.dataset.targetQuote;
document.querySelector(target_tab).classList.add("active");
e.target.src = require(`#/assets/img/testimonials/${target_img}_active.png`);
}
}
};
The button should call a method when clicked:
<button #click="someMethod">Show Content</button>
Then you can just call that method programmatically from a lifecycle hook instead of trying to manually trigger a click on the button:
methods: {
someMethod() {
console.log('someMethod called');
}
},
created() {
this.someMethod(); // Run the button's method when created
}
EDIT to match your edit:
You are using DOM manipulation but should manipulate data instead and let Vue handle the DOM. Here is a basic example of how you can do what you want:
new Vue({
el: "#app",
data() {
return {
logos: [
{
urlInactive: 'https://via.placeholder.com/150/000000/FFFFFF',
urlActive: 'https://via.placeholder.com/150/FFFFFF/000000',
isActive: false
},
{
urlInactive: 'https://via.placeholder.com/150/666666/FFFFFF',
urlActive: 'https://via.placeholder.com/150/999999/000000',
isActive: false
}
]
}
},
methods: {
toggleActive(logo) {
logo.isActive = !logo.isActive;
}
},
});
<div id="app">
<a v-for="logo in logos" #click="toggleActive(logo)">
<img :src="logo.isActive ? logo.urlActive : logo.urlInactive" />
</a>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Click events in DevExtreme DxDataGrid buttom column don't trigger

I have the following code:
<template>
<div>
<DxDataGrid :dataSource="viewerRoles">
<DxColumn data-field="name"
caption="Ansicht" />
<DxColumn data-field="description"
caption="Beschreibung" />
<DxColumn type="buttons">
<DxButton icon="preferences"
#click="test" />
</DxColumn>
</DxDataGrid>
<button #click="test"></button>
</div>
</template>
<script>
import { DxDataGrid, DxColumn, DxButton } from 'devextreme-vue/data-grid'
export default {
name: 'Test',
components: {
DxDataGrid, DxColumn, DxButton
},
data() {
return {
viewerRoles: []
}
},
async created() {
const svcResp = await this.$http.get('Settings/ViewerRoles');
if (svcResp.status === 200)
this.viewerRoles = svcResp.data;
},
methods: {
test() { alert('') }
}
}
</script>
The strange thing is, that if I click the HTML <button>, the browser shows the alert. But on the <DxButton>, it doesn't. As far as I can see, there are no errors in the debugging console.
What's wrong with my code?
Change the DxButton's #click to :on-click instead so your code should be something like below:
<DxColumn type="buttons">
<DxButton icon="preferences"
:on-click="test" />
</DxColumn>

Automatically update the input field within a form, vue.js

I want to search for elements using an input field within a form. I am passing a value from a component to another and the content changes but only after pressing enter. Is there a way to automatically update the list, after every new letter is typed?
Here is my code:
Parent(App):
<template>
<div id="app">
<Header v-on:phrase-search="passPhrase" />
</div>
</template>
<script>
import Header from ...
export default {
name: "App",
components: {
Header,
},
data() {
return {
posts: [],
searchedPhrase: ""
};
}
computed: {
filteredPosts() {
let temp_text = this.searchedPhrase;
temp_text.trim().toLowerCase();
return this.posts.filter(post => {
return post.name.toLowerCase().match(temp_text);
});
}
},
methods: {
passPhrase(phrase) {
this.searchedPhrase = phrase;
}
}
};
</script>
Child(Header):
<template>
<div class="child">
<p>Search:</p>
<form #submit.prevent="phrasePassed">
<input type="text" v-model="phrase" />
</form>
</div>
</template>
<script>
export default {
name: "search",
data() {
return {
phrase: ""
};
},
methods: {
phrasePassed() {
this.$emit("phrase-search", this.phrase);
}
}
};
</script>
passPhrase() brings the value from the child to the parent and then filteredPosts() find appropriate elements. I suspect that the form might be guilty of this issue but I do not know precisely how to get rid of it and still be able to pass the value to the parent.
Thanks
in the child you use submit event which called on enter. you should use #input on the input itself. and btw you didnt need even to declare pharse in the data because you didnt use it in the child. you just pass it up
you it like so
<template>
<div class="child">
<p>Search:</p>
<form>
<input type="text" #input="phrasePassed">
</form>
</div>
</template>
<script>
export default {
name: "search",
methods: {
phrasePassed(e) {
this.$emit("phrase-search", e.target.value);
}
}
};
</script>

Categories

Resources