Cannot receive event from child component vue3.0 - javascript

I have a button component which is a child component
<template>
<div class="button-container">
<el-button #click="$emit('onClick')" type="primary">{{ text }}</el-button>
</div>
</template>
I want to emit the click event in order for parent component to receive.
Here is the parent component
<script>
import Button from "#/components/Button.vue";
export default {
components: { Button } ,
methods: {
toRegister() { this.$router.push('/register'); }
}
}
</script>
<template>
<Button #on-click="toRegister" text="Register here" textColor="#5fb878" textSize="8px"></Button>
</template>
But i don't receive any thing from here.
I thought the problem is on the event name, but after i changed the name from onClick to clicked, same problem still.

Try to rename your custom component from Button to something else :
const { ref } = Vue
const app = Vue.createApp({
methods: {
toRegister() {
console.log('clicked')
//this.$router.push('/register');
}
}
})
app.component('btn', {
template: `
<div class="button-container">
<button #click="$emit('onClick')" type="primary">text</button>
</div>
`
})
app.mount('#demo')
<script src="https://cdn.jsdelivr.net/npm/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<btn #on-click="toRegister" text="Register here" textColor="#5fb878" textSize="8px"></btn>
</div>

Related

vue.js: create component from string

I have two components named component-1 and component-2, along with this vue.js code:
<template>
<div id="app">
<input class="input" #keyup="update()">
<div class="render"></div>
</div>
</template>
<script>
export default {
methods:{
update () {
document.querySelector(".render").innerHTML=`<component-${
document.querySelector(".input").value
} />`
}
}
}
</script>
Whenever I run the code, the component doesn't render. Is there a way to get this component to render?
This isn't the right way to render components dynamically in Vue - instead you should use :is
<template>
<div id="app">
<input class="input" #keyup="update($event.target.value)">
<component :is="myComponent"></component>
</div>
</template>
<script>
export default {
data() {
return {
myComponent: undefined
}
},
methods:{
update (component) {
this.myComponent = component
}
}
}
</script>
A sandbox demo is here

Popup does not open in App.vue when clicking on a search result

My goal is when clicking on any search item to open the Popup.
Why does not work — this.$emit('openPopup', bookId); in the method selectBook(bookId)
There is a component of Results.vue, which displays search results by using Google Books API:
<template>
<div class="results">
<ul class="results-items">
<li
class="book"
#click="selectBook(result.id)"
>
<img
:src="'http://books.google.com/books/content?id=' + result.id + '&printsec=frontcover&img=1&zoom=1&source=gbs_api'"
class="cover"
>
<div class="item-info">
<div class="bTitle">{{ result.volumeInfo.title }}</div>
<div
class="bAutors"
v-if="result.volumeInfo.authors"
>
{{ result.volumeInfo.authors[0] }}
</div>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['result'],
data() {
return {
bookId: '',
};
},
methods: {
selectBook(bookId) {
this.$router.push(`bookId${bookId}`);
this.$store.dispatch('selectBook', bookId);
this.$emit('hideElements', bookId);
this.$emit('openPopup', bookId);
},
},
};
</script>
Rusults screenshot
Is the main App.vue, which I want to display Popup:
<template>
<div id="app">
<div class="container">
<Header />
<popup
v-if="isOpenPopup"
#closePopup="closeInfoPopup"
#openPopup="showModal"
/>
<router-view/>
</div>
</div>
</template>
<script>
import Header from '#/components/Header.vue';
import Popup from '#/components/Popup.vue';
import 'bootstrap/dist/css/bootstrap.css';
export default {
components: {
Header,
Popup,
},
data() {
return {
isOpenPopup: false,
};
},
methods: {
showModal() {
console.log('click');
this.isOpenPopup = true;
},
closeInfoPopup() {
this.isOpenPopup = false;
},
},
};
</script>
The component Popup.vue
<template>
<div class="popup">
<div class="popup_header">
<span>Popup name</span>
</div>
<div class="popup_content">
<h1>Hi</h1>
<slot></slot>
<button #click="closePopup">Close</button>
</div>
</div>
</template>
<script>
export default {
name: 'popup',
props: {},
data() {
return {};
},
methods: {
closePopup() {
this.$emit('closePopup');
},
},
};
</script>
You are listening on popup component but triggering events on Result
Don't remember that events are bound to component.
You have several options how to handle it
Add event listeners (like #openPopup) only on Result component and use portal-vue library to render popup on top level
use global events, this.$root.emit and listen also on this.$root. In this case you add event listener in mount() hook and don't forget unregister it in beforeDestroy() hook
you can use Vuex and popup visibility and related data in global application store provided by Vuex

raising an event from a child component to a container component in Vue.js?

I have a child Vue component (SearchBox.Vue) that is a simple text box that will fire filterByEvent when the user hits the Enter key. In the container components, I should get the string in the text box and execute a function filterList to filter the customer's list based on the name entered. My question is how to pass the text entered in the text box to the parent component?
SearchBox.vue
<template>
<div class="SearchBox">
<input type="text" id="SearchBox" placeholder="Search"
v-on:keyup.13="$emit('FilterByEvent', $event.target.value)" :value="value" >
<font-awesome-icon icon="search" />
</div>
</template>
<script>
export default {
name: 'SearchBox',
data() {
return {
value: ''
}
}
}
</script>
<style scoped>
.SearchBox {
display: flex;
}
</style>
the container code
<template>
<div>
<div>
<SearchBox #FilterByEvent="filterList(value)"></SearchBox>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import SearchBox from '#/components/templates/SearchBox.vue'
#Component({
components: {
SearchBox
}
})
export default class ContactList extends Vue {
data() {
return {
filterString: ''
}
}
filterList(filterStr :string) {
this.$data.filterString = filterStr;
}
}
</script>
The event is not working because the component is listening on a camel case event. Here is the explanation on why this doesn't work. Plus the #FilterByEvent expects a function to execute. So use instead
<SearchBox #filter-by-event="filterList"></SearchBox>
and emit the event this way:
<div class="SearchBox">
<input type="text" id="SearchBox" placeholder="Search"
v-on:keyup.13="$emit('filter-by-event', $event.target.value)" :value="value" >
I have found that passing the $event itself will pass the text box value
<SearchBox #FilterByEvent="filterList($event)"></SearchBox>

Vue.js add function to a component

I am using Vue.js and I would like to add a function to my component, Map.vue, so when I click the button it calls a function declared in the same file :
<template>
<div>
<div class="google-map" :id="mapName">
</div>
<button >Do stuff</button>
</div>
</template>
I have only seen examples when the function is declared in the App.vue. How do you do that in my Map.vue file ?
Events
Both App.vue and Map.vue are components so they work the same for calling functions (methods) "in the same file" (single file components).
Trigger an event by adding v-on:click="yourFunctionHere()" and then make sure to declare your function in the methods object of the script section:
Map.vue
<template>
<div>
<div class="google-map" :id="mapName">
</div>
<button v-on:click="doStuff()">Do stuff</button>
</div>
</template>
<script>
export default {
methods: {
doStuff () {
alert('Did something!')
}
}
}
</script>
Custom Events
Since it's a little unclear what confused you about the child component Map.vue (since you understand the parent App.vue), perhaps you are asking how to call a function in App.vue from a button in the child Map.vue?
If that's the case, then you need to use custom events for child to parent communication:
Map.vue
<template>
<div>
<div class="google-map" :id="mapName">
</div>
<button v-on:click="doStuff()">Do stuff</button>
</div>
</template>
<script>
export default {
methods: {
doStuff () {
this.$emit('childStuff', 'some value from child')
}
}
}
</script>
App.vue
<template>
<div>
<Map v-on:childStuff="value => { parentStuff(value) }"></Map>
</div>
</template>
<script>
import Map from './components/Map'
export default {
components: {
Map
},
methods: {
parentStuff (value) {
alert(value)
}
}
}
</script>

Access component data in other component using slots in VueJS

I am trying to build a VueJS component. I have two other components which I want to use in a parent child manner. Now, I want to pass parent's data to child. It is like this:
App.vue
<template>
<div id="app">
<parent>
<child></child>
<child></child>
<child></child>
<child></child>
</parent>
</div>
</template>
<script>
import child from './child.vue'
import parent from './parent.vue'
var vm = new Vue({
el: '#app',
components: {
parent,
child
}
})
</script>
parent.vue
<template>
<div class="container">
This is parent component
<slot>
</slot>
</div>
</template>
<script>
export default {
name: 'parent',
data () {
return {
parentMsg: {
'key1': 'value1',
'key2': 'value2'
}
}
}
}
</script>
child.vue
<template>
<div class="container">
Child component
<!-- I have to place a div here which will consume parentMsg object -->
<!-- Each instance of child component rendered should have access to parentMsg -->
<div>
<!-- Some HTML to consume the parentMsg -->
</div>
</div>
</template>
<script type="text/javascript">
export default {
name: 'child',
data () {
return {
demo: 'Demo message changed'
}
}
}
</script>
I want parentMsg from parent.vue to be accessible in child.vue. I don't want to expose parentMsg on App.vue. How can I do this?
I placed the div consuming parentMsg inside (and after too) unnamed slot in parent.vue template and thought that with each instance of child.vue, a copy of div will be rendered. It does not work.
You can pass data in slot in parent.vue
<template>
<div class="container">
This is parent component
<slot :parentMsg="parentMsg"></slot>
</div>
</template>
In app.vue access parentMsg through scope and pass as props to child:
<template>
<div id="app">
<parent>
<template scope="defaultSlotScope">
<child :msg="defaultSlotScope.parentMsg"></child>
<child :msg="defaultSlotScope.parentMsg"></child>
<child :msg="defaultSlotScope.parentMsg"></child>
<child :msg="defaultSlotScope.parentMsg"></child>
</template>
</parent>
</div>
</template
>
In child.vue use msg as props:
<template>
<div class="container">
<div>
{{msg}}
</div>
</div>
</template>
<script type="text/javascript">
export default {
name: 'child',
props:['msg'],
data () {
return {
demo: 'Demo message changed'
}
}
}
</script>
Why dont you put the child-component in the parent template and pass the msg from parent to them ? Slots are not intended to be used like this.
App.vue
<template>
<div id="app">
<parent>
</parent>
</div>
</template>
<script>
import parent from './parent.vue'
var vm = new Vue({
el: '#app',
components: {
parent,
child
}
})
</script>
Parent.vue
<template>
<div class="container">
This is parent component
<child :msg="key1"></child>
<child :msg="key1"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
name: 'parent',
data () {
return {
parentMsg: {
'key1': 'value1',
'key2': 'value2'
}
}
}
}
</script>
Child.vue
<template>
<div class="container">
Child component
<div>
{{msg}}
</div>
</div>
</template>
<script type="text/javascript">
export default {
name: 'child',
props: {
msg: String
},
data () {
return {
demo: 'Demo message changed'
}
}
}
</script>

Categories