Vue3 (SFC api) event 'emit' is not propagated to parent - vue.js

I have created a very basic sample to find out how to correctly send an event from child to parent.
app.vue:
<script setup>
import HelloWorld from './components/HelloWorld.vue'
function clicked() {
alert("clicked in parent") // never gets here
}
</script>
<template>
<header>
<HelloWorld msg="Click me!" #click_text="clicked" />
</header>
</template>
HelloWorld.vue:
<script setup>
const emit = defineEmits(['clickText'])
defineProps({
msg: {
type: String,
required: true
}
})
function click() {
// alert("clicked in child") // this works, so we got here
emit('clickText')
}
</script>
<template>
<button #click="click">{{ msg }} </button>
</template>
...but the event doesn't reach the parent - what have I missed?

You have a wrong.
<HelloWorld msg="Click me!" #click_text="clicked" />
In this line, you should not put _ for click_text, you should use - instead.
<HelloWorld msg="Click me!" #click-text="clicked" />

Related

nuxt 3 triggers changes from other components

I have a question regarding triggering other components in nuxt 3. In nuxt 2 I can use $root.$refs to trigger components, but what about nuxt 3?
Example :
in componentA I have a button that triggers a popup
<template>
<button #click="openModal">Open</button>
<div appear :show="isOpen">
<component-b />
test
</div>
</template>
<script setup>
const isOpen = ref(false);
function openModal() {
isOpen.value = true;
}
</script>
in componentB.vue I have a close button for popup in componentA
<button type="button" #click="closeModal">Close</button>
My goal is when the button from component B is clicked it can trigger the popup from component A to close.
You could use an emit to do so.
In your component B, define an emit like:
// Component B
<template>
<button type="button" #click="emit('close')">Close</button>
</template>
<script setup lang="ts">
const emit = defineEmits(['close'])
</script>
You can use this emit from your Component A to close the modal.
// Component A
<template>
<button #click="openModal">Open</button>
<div appear :show="isOpen">
<component-b #close="isOpen = false" />
test
</div>
</template>
<script setup>
const isOpen = ref(false);
function openModal() {
isOpen.value = true;
}
</script>

Passing the default value of props to data in #Component

Having the code, I am getting State: undefined and clicking the toggle button does nothing.
How to get the default value from #Prop to some data correctly using #Component?
<script lang="ts">
import {
Component,
Prop,
Vue,
} from 'vue-property-decorator';
#Component
export default class HelloWorld extends Vue {
#Prop({ type: Boolean, default: true })
expanded!: boolean;
isExpanded = this.expanded;
// if its isExpanded = true; then toggling works.
}
</script>
<template>
<div class="greetings">
State: {{ isExpanded }}
<br />
<button #click="isExpanded = !isExpanded">Toggle</button>
</div>
</template>
You should not mutate the prop. Instead send a message to the parent, and let the parent mutate it.
<button #click="$emit('update:expanded', !expanded)">Toggle</button>
<hello-world :expanded='theValue' #update:expanded='theValue=$event' />

How to send a data parameter to a component?

I have one component that takes a string as input. In one of its instances, I'm sending it a string literal; and to the other one, I want to send a data parameter. How can I achieve this?
App.vue
<template>
<div>
<HelloWorld msg="What's up, man?" />
<HelloWorld msg="{{message}}" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "App",
data() {
return {
message: "Nothing much, doing OK"
}
},
components: {
HelloWorld
}
};
</script>
HelloWorld
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String
}
};
</script>
This is what I get as a result:
Any ideas? Where am I going wrong? I have looked at similar questions, but I couldn't find anything concrete.
The v-bind directive is used to bind data properties:
<HelloWorld v-bind:msg="message" />
<!-- OR shorthand -->
<HelloWorld :msg="message" />
demo

Vue - Unable pass specific value to higher component

I am getting the following - Cannot read property 'free' of undefined.
I will be adding this button component on multiple pages and I have data object which will allow me to add text based on whatever page I want displayed on a page. For example if its on the homepage I would like to use <buttons :text="buttonText.free" /> and on about us page I would like to use <buttons :text="buttonText.spend" />
Template file
<template>
<main class="container">
<buttons :text="buttonText.free" />
</main>
</template>
<script>
import Buttons from '~/components/atoms/buttons.vue'
export default {
components: {
Buttons
}
}
</script>
Component file
<template>
<div>
<button class="button"">
<span>{{ buttonText }}</span>
</button>
</div>
</template>
<script>
export default {
props: {
text: String
},
data () {
return {
buttonText: {
free: 'free',
spend: 'spend',
now: 'now',
nowFree: 'now free'
}
}
}
}
</script>
Could you tell me what I am doing wrong?
You should define your data in your parent component's data property. All the variables that is used inside the template tag will be fetched from data, computed or props of the component. You are passing an undefined buttonText data to your buttons component.
<template>
<main class="container">
<buttons :text="buttonText.free" />
</main>
</template>
<script>
import Buttons from '~/components/atoms/buttons.vue'
export default {
data() {
return {
buttonText: {
free: 'free',
spend: 'spend',
now: 'now',
nowFree: 'now free'
}
}
},
components: {
Buttons
}
}
</script>
and in your buttons component, just accept the props passed by the parent component. In this case, you are using text as the props of the buttons component.
<template>
<div>
<button class="button"">
<span>{{ text }}</span>
</button>
</div>
</template>
<script>
export default {
props: {
text: String
}
}
</script>
template.vue
<template>
<main class="container">
<buttons :text="your customized text" />
</main>
</template>
<script>
import Buttons from '~/components/atoms/buttons.vue'
export default {
components: {
Buttons
}
}
</script>
buttons.vue
<template>
<div>
<button class="button">
<span>{{ text }}</span>
</button>
</div>
</template>
<script>
export default {
props: {
text: String
}
}
</script>
here is a simple solution to solve your problem
but you need to learn more fundamentals on vue components
vue component doc

vuejs passing method to child dynamically

I have a 3rd party component <third-party-component /> which accepts following event:
onAdd, onRemove, onUpdate
I want to create a wrapper component around it and want to pass these events dynamically so that can handle the response in wrapper component, something like
wrapper.js
<template>
<div class="my-wrapper">
<third-party-component />
</div>
<template>
using-wrapper.js
<template>
<div>
...
<wrapper #onAdd="add" #onRemove="remove"></wrapper>
...
</div>
</template>
<script>
export default {
methods: {
onAdd() {
console.log('on add in using-wrapper.js');
},
onRemove() {
console.log('on remove in using-wrapper.js');
}
}
}
</script>
You can pass all attributes and listeners by binding them using v-bind and v-on
You also need to set inheritAttrs to false
https://v2.vuejs.org/v2/api/#inheritAttrs
<template>
<div class="my-wrapper">
<third-party-component v-bind="$attrs" v-on="$listeners"/>
</div>
<template>
using-wrapper.js
<template>
<div>
...
<wrapper #onAdd="add" #onRemove="remove"></wrapper>
...
</div>
</template>
<script>
export default {
inheritAttrs: false,
methods: {
onAdd() {
console.log('on add in using-wrapper.js');
},
onRemove() {
console.log('on remove in using-wrapper.js');
}
}
}
</script>
Well on "wrapper" you will need to create 3 methods that listen and get triggered on "third-party-component".
<template>
<div class="my-wrapper">
<third-party-component #onAdd="wrapperAdd" #onRemove="wrapperRemove" #onUpdate="wrapperUpdate" />
</div>
<script>
export default {
methods:{
wrapperAdd(){
this.$emit("onAdd",{obj that will get to the parent});
}
}
}
I made only 1 method because the other 2 are similar.