Vue $emit not firing inside parent - javascript

For some reason my $emit doesn't seem to be firing inside the parent. I am basically trying to create a modal popup for a html form. Inside my header component I have a button that fires $emit I then try to listen to this event inside my app.js on the form component. But the form component is doing nothing when the emit is fired.
Here's my code
client/src/app.js
<template>
<div id="app">
<MainHeader :modalVisability="modal" />
<OppForm :modalVisability="modal" v-on:showModal="modal = $event"/>
<div>{{ modal }}</div>
</div>
</template>
<script>
import MainHeader from './components/MainHeader.vue';
import OppForm from './components/oppForm.vue';
export default {
name: 'App',
components: {
MainHeader,
OppForm
},
data() {
return {
modal: false
}
}
}
</script>
client/components/MainHeader.vue
<template>
<div id="main_header_wrap">
<header>
<button v-on:click="showOppForm">Add Post</button>
</header>
<div>{{ modalVisability }}</div>
</div>
</template>
<script>
export default {
props: {
modalVisability: Boolean
},
methods: {
showOppForm() {
this.modalVisability = true;
this.$emit('showModal', this.modalVisability);
}
},
}
</script>
client/src/components/oppForm.vue
<template>
<div id="opp_form" >
<form #submit.prevent="SubmitOpp" v-if="modalVisability">
<input type="text" name="company_name" v-model="company_name">
<button type="submit">Submit</button>
</form>
<div>{{ modalVisability }}</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'oppForm',
props: {
modalVisability: Boolean,
},
data() {
return {
company_name: ''
}
},
methods: {
SubmitOpp() {
axios.post('http://localhost:5000/', {
company_name: this.company_name,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
}
</script>

I fixed somethings in your code. See the example below:
client/src/app.js
<template>
<div id="app">
<MainHeader :modalVisability="modal" #showModal="changeModal" />
<OppForm :modalVisability="modal" />
<div>App = {{ modal }}</div>
</div>
</template>
<script>
import MainHeader from './components/MainHeader.vue';
import OppForm from './components/oppForm.vue';
export default {
name: 'App',
components: { MainHeader, OppForm },
data() {
return {
modal: false,
};
},
methods: {
changeModal(newValueModal) {
this.modal = newValueModal;
},
},
};
</script>
client/components/MainHeader.vue
<template>
<div id="main_header_wrap">
<header>
<button v-on:click="showOppForm">Add Post</button>
</header>
<div>MainHeader = {{ modalVisability }}</div>
</div>
</template>
<script>
export default {
props: {
modalVisability: Boolean,
},
methods: {
showOppForm() {
this.$emit('showModal', !this.modalVisability);
},
},
};
</script>
client/src/components/oppForm.vue
<template>
<div id="opp_form">
<form #submit.prevent="SubmitOpp" v-if="modalVisability">
<input type="text" name="company_name" v-model="company_name" />
<button type="submit">Submit</button>
</form>
<div>oppForm = {{ modalVisability }}</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'oppForm',
props: {
modalVisability: Boolean,
},
data() {
return {
company_name: '',
};
},
methods: {
SubmitOpp() {
axios
.post('http://localhost:5000/', {
company_name: this.company_name,
})
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});
},
},
};
</script>
1 - App.js: Listen the event in MainHeader component.
2 - App.js: OppForm don't need to listen the event, because this component don't change the modal value.
3 - MainHeader.vue: Avoid to change the props value.

Related

how to call a function from another component in vue3?

i have tried to using $on method and this.$root.$refs.compname_component = this; but got some errors,Please refer below my codes
formComponent.vue
<template>
<div v-if="showForm">
create Form
</div>
</template>
<script>
export default {
props:[],
setup(props) {
return props;
},
data() {
return {
formData:{},
showForm:false
}
},
created() {
// this.$root.$refs.tableCommon = this;
// this.$root.$refs.compname_component = this;
},
mounted() {
console.log('form mounted');
// this.$root.$on("displayForm", () => {
// this.displayForm();
// });
},
methods: {
displayForm:function(){
this.showForm = true;
}
},
}
</script>
commonComponent.vue
<template>
<div class="col-10 text-end custom-inline-spacing mb-3">
<button type="button" class="btn btn-outline-secondary" #click="showCreateForm">Create</button>
</div>
</template>
<script>
export default {
props: [],
setup() {
return {}
},
data() {
return {
}
},
mounted() {
console.log('mounted common')
},
methods: {
showCreateForm : function(){
// this.$refs.form.displayForm();
// this.$root.$refs.compname_component.displayForm();
// this.$root.$emit("displayForm");
this.createForm.displayForm();
}
}
}
</script>
app.js
require('./bootstrap')
import { createApp } from 'vue'
import tableCommon from './components/CommonComponent'
import createForm from './components/formComponent';
const app = createApp({})
app.component('vue-common', tableCommon);
app.component('create-form', createForm);
app.mount('#app')
actualy what i want means call formComponent.displayForm() from CommonComponent.
If you want to conditionally display child component, you can move logic out of that component into the parent component.
Parent.vue
<template>
<FormComponent v-if="showComponent" />
</template>
Alternatively, you can pass a prop into FormComponent if you need to conditionally display only a part of that component.
Parent.vue
<template>
<FormComponent v-bind:show-component="showComponent" />
</template>
FormComponent.vue
<template>
<div v-if="showComponent">
...
</div>
<div>
...
</div>
</template>

Undefined prop in child component

I have a parent component containing three child components. The first child component is a form. On a submit event it passes data to both the second and third child components via the parent component using props. However in one of the child components, the prop is always undefined. I think it's a timing issue, but using v-if does not seem to solve the issue.
The Parent Component:
<template>
<div>
<patents-searchform v-on:form-submit="processForm"></patents-searchform>
<patents-word-cloud
v-if="searched"
v-show="searched"
:patentsQuery="patentsQuery"
:livePage="livePage"
v-on:pageChange="handlePageChange"
/>
<patents-search-results
v-if="searched"
v-show="searched"
ref="resultsRef"
:livePage="livePage"
:results="patentsQueryResult"
v-on:pageChange="handlePageChange"
</div>
</template>
export default {
data() {
return {
livePage: 1,
searched: false,
queryform: 'initVal',
patentsQueryResult: {},
searching: false,
patentsQuery: {}
};
},
components: {
'patents-searchform': PatentsSearchForm,
'patents-searchresults': PatentsSearchResults,
'patents-word-cloud': PatentsWordCloud,
},
methods: {
handlePageChange(value) {
console.log('Homepage::handlePageChange', value)
this.queryform.page = value;
this.livePage = value;
this.fetchData();
},
processForm(formData) {
this.queryform = formData;
this.fetchData();
this.patentsQuery['query'] = this.queryform['query']
this.patentsQuery['searchMode'] = this.queryform['searchMode']
this.searched = true;
},
fetchData() {
const path = '/flask/searchPatentsNEW';
this.searching = true;
if (this.queryform !== 'initVal') {
axios.post(path, this.queryform)
.then((res) => {
this.patentsQueryResult = res.data;
this.searching = false;
})
.catch((error) => {
console.log(error);
});
}
}
}
};
The child component (PatentSearchResults) in which the props work correctly:
<template>
<b-container>
<b-card>
<a id="sTitleCard">Search Results</a>
<div id="quickStats" style="margin-left: 5%" v-if="this.results.stats">
{{results.stats.totalAuthors}} inventors across {{results.stats.docCount}} patents
({{results.stats.totalQueryTime}} seconds)
</div>
</b-card>
</b-container>
</template>
<script>
export default {
name: 'Results',
props: ['results', 'livePage'],
computed: {
thisAuthorPage() {
if (this.results.authors !== null) {
return this.results.authors; //this.results works fine
}
console.log('no authors')
return [];
},
},
methods: {
},
};
</script>
And the child component where the props are undefined:
<template>
<div>
<b-card id="patentWordCloudCard" bg-variant="light">
<b-container>
<b-form id="queryForm" #submit="onSubmit" #reset="onReset" novalidate>
<b-row class="d-flex flex-row-reverse">
<b-button id="btnSubmit" type="submit" variant="primary">Generate query word cloud</b-button>
</b-row>
</b-form>
</b-container>
</b-card>
</div>
</template>
<script>
export default {
name: 'Patents Word Cloud',
props: ['patentsQuery'],
data() {
return{
form: {
query: this.patentsQuery.query,
searchMode: this.patentsQuery.searchMode
},
show: true,
}
},
mounted() {
console.log(this.patentsQuery) //undefined
},
computed() {
console.log(this.patentsQuery) //undefined
},
methods: {
onSubmit(evt) {
evt.preventDefault();
console.log(this.patentsQuery) //undefined
}
}
}
</script>
Is it a timing issue where the word cloud component is mounted before patentsQuery is defined? If so, why did v-if not delay the component, as searched is false until after patentsQuery is defined.
It was a timing issue, I was able to access patentsQuery with the following code:
<template>
<div>
<b-card id="patentWordCloudCard" bg-variant="light">
<b-container>
<b-form id="queryForm" align-h="center" #submit="onSubmit" #reset="onReset" novalidate>
<b-row align-h="center">
<b-button align-h="center" id="btnSubmit" type="submit" variant="primary">Generate query word cloud</b-button>
</b-row>
</b-form>
</b-container>
</b-card>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Patents Word Cloud',
props: ['patentsQuery'],
methods: {
onSubmit(evt) {
evt.preventDefault();
const path = 'flask/patentWordCloud';
this.searching = true
axios.post(path, this.patentsQuery).then((res) => {
console.log('Patent Word Cloud Data')
console.log(res.data)
this.patentResults = res.data;
})
.catch((error) => {
console.log(error)
})
}
}
}
</script>

Deleting an object in Vue, emitting from child to parent and to parent

I have created a simple to do app in VUE.
In order to delete a card (each card is an object with an id, title and description located in state in an App.vue component), I am passing and id from App as a prop to TaskList and to the button (delete) in the Task component. Then in order trigger a deleteTask function, again I am emmiting an id from Task to TaskList and then to App.
This approach works. However, is that kind of long chain of emmiting is considered as good practice? Is there a better way to do it?
App.vue
<template>
<div>
<TaskList :tasks="tasks" #id="deleteTask"/>
<Form :length="tasks.length" #addTask="addNewTask" />
</div>
</template>
<script>
import TaskList from './components/TaskList';
import Form from './components/Form';
export default {
name: 'App',
components: { TaskList, Form },
data() {
return {
tasks: [
{
id: 1,
title: 'Hello World',
description: 'this is the world'
},
{
id: 2,
title: 'Hello Mars',
description: 'this is the mars'
},
{
id: 3,
title: 'Hello Jupiter',
description: 'this is the jupiter'
}
]
}
},
methods: {
addNewTask(taskObject) {
const listOfTasks = this.tasks.concat(taskObject);
this.tasks = listOfTasks;
},
deleteTask(id) {
const filteredList = this.tasks.filter(element => {
return element.id != id;
})
this.tasks = filteredList;
}
}
}
</script>
TaskList.vue
<template>
<div class="taskList" v-bind:key="task" v-for="task in tasks">
<Task :title="task.title" :description="task.description" :id="task.id" #id="sendId"/>
</div>
</template>
<script>
import Task from './Task';
export default {
props: ['tasks'],
components: { Task },
methods: {
sendId(id) {
this.$emit('id', id);
console.log(id)
}
}
}
</script>
Task.vue
<template>
<div class="task">
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<button #click="passId">Delete</button>
</div>
</template>
<script>
export default {
props: ['title', 'description', 'id'],
methods: {
passId() {
this.$emit('id', this.id);
}
}
}
</script>
One sure way of reducing this chain of data transfer is by using Vuex, But if you don't want to use that you can also use an "EventBus"
NOTE - Still you will have to pass the id from parent to child
Creating event bus
// src > eventBus.js
import Vue from 'vue'
export default new Vue()
Emit the event when the user clicks on the delete button
// Task.vue
<template>
<div class="task">
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<button #click="passId">Delete</button>
</div>
</template>
<script>
import EventBus from 'path/to/eventBus'
export default {
props: ['title', 'description', 'id'],
methods: {
passId() {
EventBus.$emit('delete-task', this.id);
}
}
}
</script>
Listen to the event on the topmost parent
<template>
<div>
<TaskList :tasks="tasks" #id="deleteTask"/>
<Form :length="tasks.length" #addTask="addNewTask" />
</div>
</template>
<script>
import TaskList from './components/TaskList';
import Form from './components/Form';
import EventBus from 'path/to/eventBus.js'
export default {
name: 'App',
components: { TaskList, Form },
data() {
return {
tasks: [
{
id: 1,
title: 'Hello World',
description: 'this is the world'
},
{
id: 2,
title: 'Hello Mars',
description: 'this is the mars'
},
{
id: 3,
title: 'Hello Jupiter',
description: 'this is the jupiter'
}
]
}
},
mounted(){
// Listening to the delete-task event
EventBus.$on('delete-task', (id) => {
this.deleteTask(id)
})
},
methods: {
addNewTask(taskObject) {
const listOfTasks = this.tasks.concat(taskObject);
this.tasks = listOfTasks;
},
deleteTask(id) {
const filteredList = this.tasks.filter(element => {
return element.id != id;
})
this.tasks = filteredList;
}
}
}
</script>

VUEJS - Bind the return from my method to my child template

I need to bind the return from my method to my child template.
methods object
methods: {
filterOptions(checkedValues: any) {
let filtered = this.cards.filter(card => {
return card.profile
.map(profile => profile.salary)
.find(profileSalary => {
return checkedValues.find((salaryOption: number) => {
return profileSalary >= salaryOption;
});
});
});
}
}
template
<template>
<section id="app">
<Gallery v-bind:filtered="filtered"/>
</section>
</template>
You can pass an array of objects as a parameter to the child component.
See the docs.
Child
<template>
<div>
<span v-for="item in arr"
:key="item.id">{{ item.name }}</span>
</div>
</template>
<script>
export default {
name: 'Child',
params: {
arr: {
type: Array,
default: () => []
}
}
}
</script>
Parent
<template>
<div>
<child :arr="parentArray"/>
</div>
</template>
<script>
import Child from './Child.vue'
export default {
name: 'Parent',
components: {
Child
},
computed: {
parentArray () {
return [...whatever]
}
}
}
</script>

Passing errors to error-form component?

How do pass errors from ajax to error-form component?
FormLink.vue component:
<template>
<error-form></error-form>
<form class="form-horizontal">
.
.
</form>
</template>
<script>
export default {
methods: {
addLink: function (event) {
event.preventDefault();
this.$http.post('/user/link', {name: this.linkName, key: this.linkValue}).then(response => {
this.users.links.push(response.json());
this.linkName = '';
this.linkValue = '';
}).error(function(errors) {
// how to pass errors data to <error-form> component?
});
},
}
}
</script>
ErrorForm.vue component
<template>
<div class="alert alert-danger">
<strong>Errors!</strong><br><br>
<ul>
<li></li>
</ul>
</div>
</template>
<script>
export default {
ready() {
console.log('Error Component ready.');
},
data: function () {
return {
}
},
}
</script>
in App.js
Vue.component('form-link', require('./components/Profile/FormLink.vue'));
Vue.component('error-form', require('./components/Global/ErrorForm.vue'));
Pass your data using props:
FormLink Component:
<template>
<error-form :errors="errors"></error-form>
</template>
<script>
EXPORT default {
data() {
return {
errors: []
}
},
methods: {
addLink: function (event) {
event.preventDefault();
this.$http.post('/user/link', {name: this.linkName, key: this.linkValue}).then(response => {
this.users.links.push(response.json());
this.linkName = '';
this.linkValue = '';
}).error(function(errors) {
this.errors = errors;
});
},
}
}
</script>
ErrorForm Component:
<template>
<div class="alert alert-danger">
<strong>Errors!</strong><br><br>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</div>
</template>
<script>
EXPORT default {
props: {
errors: Array
},
ready() {
console.log('Error Component ready.');
},
data: function () {
return {
}
},
}
</script>

Categories

Resources