emit value from mounted in child.vue to parent.vue - javascript

I'm working with BootstrapVue.
I need to emit a value to my parent.vue - but my code line this.$emit('info', this.hide); doesn't work out.
If I console.log(this.hide) i get my value correct in this case false, otherwise if my if-statement is correct I get it true.
What is the mistake in here?
script of my child.vue:
data(){
return {
hide: true,
}
}
mounted() {
if (statement) {
if(some statement) {
//do something
} else {
this.hide = false;
console.log(this.hide); //HERE I GET CORRECT VALUE
this.$emit('info', this.hide); //THIS DOESNT WORK
}
}
}
How it should work in my parent.vue:
<template>
<div #info="info">
<div> //THIS DIV SHOULD BE SHOWN IF this.hide = false
</div>
<div> //THIS DIV SHOULD BE SHOWN IF this.hide = true
</div>
</div>
</template>

Try something like following snippet :
Vue.component('Child', {
template: `
<div class="">
child
<button #click="changeHide">change hide</button>
</div>
`,
data(){
return {
hide: true,
}
},
methods: {
changeHide() {
this.hide = !this.hide
this.sendInfo()
},
sendInfo() {
this.$emit('info', this.hide);
}
},
mounted() {
//if (statement) {
//if(some statement) {
//do something
//} else {
this.hide = false;
this.sendInfo()
//}
}
})
new Vue({
el: '#demo',
data(){
return {
info: null,
}
},
methods: {
setInfo(val) {
this.info = val
}
}
})
Vue.config.productionTip = false
Vue.config.devtools = false
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div v-if="!info"> //THIS DIV SHOULD BE SHOWN IF this.hide = false
</div>
<div v-if="info"> //THIS DIV SHOULD BE SHOWN IF this.hide = true
</div>
<p>from child info is: {{ info }}</p>
<Child #info="setInfo" />
</div>

Ideally in App.vue (Parent)
you should have something like
<login #info="someHandler"></login>
there is no need to go for a child component if you gonna jus use to manage some logic. It is appropriate only if have some contents in the template.
Also placing emit handler on some div. Thats not how emit works
Below is a simple working example
App.vue (parent)
<template>
<h1>{{ title }}</h1>
<Child #changeTitle="ChangeT($event)" />
</template>
<script>
import Child from "./components/Child"
export default{
name:'App',
components: {
Child,
},
data()
{
return{
title:'Rick Grimes'
}
},
methods:{
ChangeT(title)
{
this.title=title;
},
}
</script>
<style></style>
Child.vue
<template lang="html">
<button type="button" #click='passEvent'> Update me</button>
</template>
<script>
export default {
name:'Child',
methods:{
passEvent()
{
this.$emit('changeTitle','Awesome ')
}
}
}
</script>
<style lang="css" scoped>
</style>

Related

Vue can't receive subscribe to event from child component in to parent component

I have two components - parent and child. The child emits an event, which should trigger a console output in a parent component. However nothing is printed in console.
TestPage.vue:
<template>
<div v-on:hidden="hiddenEvent">
<TestComponent></TestComponent>
</div>
</template>
<script>
import TestComponent from "#/app/test/TestComponent";
export default {
name: "TestPage",
components: {TestComponent},
methods: {
hiddenEvent() {
console.log("hiddenEvent");
}
}
}
</script>
<style scoped>
</style>
TestComponent.vue:
<template>
<div class="test-container" v-if="!isHidden"
v-on:click="this.flip">
{{this.text}}
</div>
</template>
<script>
export default {
name: "TestComponent",
data() {
return {
text: "abc",
isHidden: false
}
},
methods: {
flip() {
this.isHidden = !this.isHidden;
if (this.isHidden) {
this.$emit("hidden");
} else {
this.$emit("revealed");
}
}
}
}
</script>
<style scoped>
.test-container {
padding: 2em;
background-color: #2EAC68;
}
</style>
When I click on the abc:
it disappears:
So I know the flip method is executed. However the parent's hiddenEvent is not executed, because hiddenEvent is never printed to console.
What am I doing wrong? I need the parent TestPage to be able to react to the event emitted by the child TestComponent
You should use v-on:hidden="hiddenEvent" on TestComponent tag not on the wrapping div.
The
<TestComponent></TestComponent>
in TestPage.vue should be updated to:
<TestComponent v-on:hidden="hiddenEvent"></TestComponent>
See in sandbox
You are listening to event from the child so put it on the child:
const app = Vue.createApp({
methods: {
hiddenEvent() {
console.log("hiddenEvent");
}
}
})
app.component('testComponent', {
template: `
<div class="test-container" v-if="!isHidden"
v-on:click="this.flip">
{{this.text}}
</div>
`,
data() {
return {
text: "abc",
isHidden: false
}
},
methods: {
flip() {
this.isHidden = !this.isHidden;
if (this.isHidden) {
this.$emit("hidden");
} else {
this.$emit("revealed");
}
}
}
})
app.mount('#demo')
.test-container {
padding: 2em;
background-color: #2EAC68;
}
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div >
<test-component #hidden="hiddenEvent"></test-component>
</div>
</div>

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'/>

How to pass initial form values to child component in Vue.js?

I'm using Vue.js. From my template I include the child component (componentB) which includes several input elements. I want to initialize those input elements from my parent template. I found a way to do this (see code below). However, I'm wondering if this is a correct way, as the articles I have read so far use different approaches (e.g. with $emit):
https://simonkollross.de/posts/vuejs-using-v-model-with-objects-for-custom-components
https://zaengle.com/blog/using-v-model-on-nested-vue-components
https://alligator.io/vuejs/add-v-model-support/
Can you confirm that my code below matches the Vue.js design concepts or are there flaws?
<template>
<div>
<div class="md-layout">
<div class="md-layout-item md-size-100">
<ComponentB ref="componentB" v-model="componentB"></ComponentB>
</div>
</div>
</div>
</template>
<script>
import { ComponentB } from "#/components";
export default {
components: {
ComponentB
},
data() {
return {
componentB: {
textInputField: "my-initial-value"
}
};
},
methods: {
validate() {
return this.$refs.componentB.validate().then(res => {
this.$emit("on-validated", res);
return res;
});
}
}
};
</script>
<style></style>
Form componentB
<template>
<div>
<md-field
:class="[
{ 'md-valid': !errors.has('textInputField') && touched.textInputField },
{ 'md-form-group': true },
{ 'md-error': errors.has('textInputField') }
]"
>
<md-icon>label_important</md-icon>
<label>My text input</label>
<md-input
v-model="textInputField"
data-vv-name="textInputField"
type="text"
name="textInputField"
required
v-validate="modelValidations.textInputField"
>
</md-input>
<slide-y-down-transition>
<md-icon class="error" v-show="errors.has('textInputField')"
>close</md-icon
>
</slide-y-down-transition>
<slide-y-down-transition>
<md-icon
class="success"
v-show="!errors.has('textInputField') && touched.textInputField"
>done</md-icon
>
</slide-y-down-transition>
</md-field>
</div>
</template>
<script>
import { SlideYDownTransition } from "vue2-transitions";
export default {
name: "componentB",
props: ['value'],
components: {
SlideYDownTransition
},
computed: {
textInputField: {
get() {return this.value.textInputField},
set(textInputField) { this.$emit('input', { ...this.value, ['textInputField']: textInputField })}
}
},
data() {
return {
touched: {
textInputField: false
},
modelValidations: {
textInputField: {
required: true,
min: 5
}
}
};
},
methods: {
getError(fieldName) {
return this.errors.first(fieldName);
},
validate() {
return this.$validator.validateAll().then(res => {
return res;
});
}
},
watch: {
textInputField() {
this.touched.runnerName = true;
}
}
};
</script>
<style></style>
The simplest way to pass data to child component is to use props, which are then available in the child component and can pass the values back up to the parent.
https://v2.vuejs.org/v2/guide/components-props.html
// PARENT COMPONENT
<ComponentB :textInputField="textInputField" ...></ComponentB>
// CHILD COMPONENT
// TEMPLATE SECTION
<md-input
v-model="textInputField"
value="textInputField"
...
>
// SCRIPT SECTION
export default {
props: {
textInputField: String
}
}

Vue.js Update child component data from within parent

How can I update the data in a child component from within the parent? I am trying to update the autores property from within the parent and have that update the child data. Currently nothing happens, I don't think I have the data linked correctly. If I add it as data to the parent component then the parent re-renders when the code runs and no results can be seen.
Parent:
<template>
<div>
<input #keyup="editName(lender.id, $event)">
<autocomplete-suggestions :autores="[]"></autocomplete-suggestions>
</div>
</template>
<script type="text/javascript">
export default {
props: ['lender'],
data(){
return {
}
},
methods: {
editName: function(lenderid, event) {
var self = this;
axios.post('/lenders/autocomplete', {...})
.then(function (data) {
self.autores = data.data.suggestions;
})
.catch(function (error) {
console.log("Error occurred getting the autocomplete");
});
},
},
watch: {
},
mounted: function() {
}
};
</script>
Child:
<template>
<div class="list">
<div v-for="(res, i) in autores">{{res}}</div>
</div>
</template>
<script type="text/javascript">
export default {
props: ['autores'],
data(){
return {
}
}
};
</script>
Update:
If I change my code in parent to:
data(){
return {
autores:[]
}
},
and:
<autocomplete-suggestions :autores="autores"></autocomplete-suggestions>
Then simply whenever I update this.autores, whatever text has been typed into the text box in parent in reset, as if it were rerendering the whole component. How can ths be stopped?
Parent:
<template>
<div>
<input #keyup="editName(lender.id, $event)">
<autocomplete-suggestions ref="autocompleteSuggestions"></autocomplete-suggestions>
</div>
</template>
<script type="text/javascript">
export default {
props: ['lender'],
methods: {
editName: function (lenderid, event) {
var self = this;
axios.post('/lenders/autocomplete', {...})
.then(function (data) {
self.$refs.autocompleteSuggestions.autores = data.data.suggestions;
})
.catch(function (error) {
console.log("Error occurred getting the autocomplete");
});
},
},
};
</script>
Child:
<template>
<div className="list">
<div v-for="(res, i) in autores">{{res}}</div>
</div>
</template>
<script type="text/javascript">
export default {
data() {
return {
autores: []
};
},
};
</script>

How do you target a button, which is in another component, with a method in App.vue?

I'm making a basic To Do app, where I have an input field and upon entering a task and pressing the "Enter" key the task appears in the list. Along with the task the TodoCard.vue component also generates a button, which I would like to use to delete the task.
I've added a #click="removeTodo" method to the button, but don't know where to place it, in the TodoCard component or in the App.vue file?
TodoCard component:
<template>
<div id="todo">
<h3>{{ todo.text }}</h3>
<button #click="removeTodo(todo)">Delete</button>
</div>
</template>
<script>
export default {
props: ['todo'],
methods: {
removeTodo: function (todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
}
}
}
</script>
App.vue:
<template>
<div id="app">
<input class="new-todo"
placeholder="Enter a task and press enter."
v-model="newTodo"
#keyup.enter="addTodo">
<TodoCard v-for="(todo, key) in todos" :todo="todo" :key="key" />
</div>
</template>
<script>
import TodoCard from './components/TodoCard'
export default {
data () {
return {
todos: [],
newTodo: ''
}
},
components: {
TodoCard
},
methods: {
addTodo: function () {
// Store the input value in a variable
let inputValue = this.newTodo && this.newTodo.trim()
// Check to see if inputed value was entered
if (!inputValue) {
return
}
// Add the new task to the todos array
this.todos.push(
{
text: inputValue,
done: false
}
)
// Set input field to empty
this.newTodo = ''
}
}
}
</script>
Also is the code for deleting a task even correct?
You can send an event to your parent notifying the parent that the delete button is clicked in your child component.
You can check more of this in Vue's documentation.
And here's how your components should look like:
TodoCard.vue:
<template>
<div id="todo">
<h3>{{ todo.text }}</h3>
<button #click="removeTodo">Delete</button>
</div>
</template>
<script>
export default {
props: ['todo'],
methods: {
removeTodo: function (todo) {
this.$emit('remove')
}
}
}
</script>
App.vue:
<template>
<div id="app">
<input class="new-todo"
placeholder="Enter a task and press enter."
v-model="newTodo"
#keyup.enter="addTodo">
<TodoCard v-for="(todo, key) in todos" :todo="todo" :key="key" #remove="removeTodo(key)" />
</div>
</template>
<script>
import TodoCard from './components/TodoCard'
export default {
data () {
return {
todos: [],
newTodo: ''
}
},
components: {
TodoCard
},
methods: {
addTodo: function () {
// Store the input value in a variable
let inputValue = this.newTodo && this.newTodo.trim()
// Check to see if inputed value was entered
if (!inputValue) {
return
}
// Add the new task to the todos array
this.todos.push(
{
text: inputValue,
done: false
}
)
// Set input field to empty
this.newTodo = ''
}
},
removeTodo: function(key) {
this.todos.splice(key, 1);
}
}
</script>

Categories

Resources