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>
Related
I have to add delete and update buttons for every table elements.
I tried adding the delete icon by using mdi-icons
but its not in every elements of the table.
So how can solve this problem with this code? Any suggestions for solving my problem inside the current code structure? Also share the best way to achieve my goal is acceptable.
This is my template view
<template>
<div>
<v-form>
<div class="align-center justify-space-between " style="display: flex;">
<v-text-field label="name" v-model="name" placeholder="Your name" required outlined></v-text-field>
<v-text-field label="address" v-model="address" placeholder="address" required outlined></v-text-field>
<v-text-field label="contact" v-model="contact" placeholder="contact" required outlined></v-text-field>
</div>
<v-btn type="submit" value="submit" #click="onSubmit" class="black--text">
{{ btnName }}
</v-btn>
</v-form>
<v-container>
<template>
<v-data-table :headers="headers" :items="allStudents" :items-per-page="10" class="elevation-1"></v-data-table>
</template>
</v-container>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
name: "AboutView",
data() {
return {
btnName: "Save",
name: '',
address: '',
contact: '',
headers: [
{
text: 'Name',
value: 'name',
},
{ text: 'Contact', value: 'contact' },
{ text: 'Address', value: 'address' }
],
};
},
methods: {
...mapActions(["addStudents", "fetchStudents"]),
onSubmit(event) {
event.preventDefault();
let data = {
name: this.name,
address: this.address,
contact: this.contact,
}
this.addStudents(data);
}
},
computed: mapGetters(["allStudents"]),
created() {
this.fetchStudents();
}
};
</script>
this is my vuex code
import Axios from "axios";
const state = {
students: [],
};
const getters = {
allStudents: (state) => {
return state.students;
},
};
const actions = {
async addStudents({ commit }, payload = {}) {
console.log("mypayload", payload);
const response = await Axios.post(
"http://127.0.0.1:8000/api/students",
payload
);
commit("addStudents", response.data);
},
async fetchStudents({ commit }) {
const response = await Axios.get("http://127.0.0.1:8000/api/students");
commit("fetchStudents", response.data);
},
};
const mutations = {
addStudents: (state, newStudent) => state.students.unshift(newStudent),
fetchStudents: (state, students) => (state.students = students),
};
export default {
state,
getters,
actions,
mutations,
};
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>
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>
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.
How to binding parent's model to child in Vue.js?
These codes below is works fine. if i fill the input manually, then child's model return it's value to the parent's model.
But the issue is, if the data set from AJAX request in a parent, the input doesn't automatically filled.
Can anyone help me on this?
Form.vue
<template>
<form-input v-model="o.name" :fieldModel="o.name" #listenChanges="o.name = $event"/>
<form-input v-model="o.address" :fieldModel="o.address" #listenChanges="o.address = $event"/>
</template>
<script>
import FormInput from '../share/FormInput.vue'
export default {
data () {
return {
o: {
name: '',
address: ''
}
}
},
components: { 'form-input': FormInput },
created: function() {
axios.get('http://api.example.com')
.then(response => {
this.o.name = response.data.name
this.o.address = response.data.address
})
.catch(e => { console.log(e) })
}
}
</script>
FormInput.vue
<template>
<input type="text" v-model='fieldModelValue' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
mounted: function() {
this.fieldModelValue = this.fieldModel;
},
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Things are more straightforward if you take advantage of v-model in components.
If you put v-model on a component, the component should take a prop named value, and should emit input events to trigger it to update.
I like to make a computed to hide the event emitting, and allow me to just v-model the computed inside my component.
new Vue({
el: '#app',
data: {
o: {
name: '',
address: ''
}
},
components: {
'form-input': {
template: '#form-input',
props: ['value'],
computed: {
fieldModelValue: {
get() {
return this.value;
},
set(newValue) {
this.$emit('input', newValue.toUpperCase());
}
}
}
}
},
// Simulate axios call
created: function() {
setTimeout(() => {
this.o.name = 'the name';
this.o.address = 'and address';
}, 500);
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
Name ({{o.name}})
<form-input v-model="o.name"></form-input>
Address ({{o.address}})
<form-input v-model="o.address"></form-input>
</div>
<template id="form-input">
<input type="text" v-model='fieldModelValue'>
</template>
The mounted() hook is blocking subsequent updates from the parent.
Remove mounted and change v-model to 'fieldModel'
<template>
<input type="text" :value='fieldModel' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
// mounted: function() {
// this.fieldModelValue = this.fieldModel;
// },
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Demo CodeSandbox