now i have 2 Components
1 - is just a drop down list v-select
<v-row align="center" >
<v-col class="d-flex" cols="12" sm="6" v-if="Compounds" >
<v-select :items="Compounds"
v-model="selectedItems"
label="Select"
item-value="id"
item-text="name"
v-on:change="selectedCompound">
</v-select>
{{ selectedItems }}
</v-col>
</v-row>
with method
methods: {
selectedCompound(h2o) {
console.log(h2o);
console.log("This is from Selected Compound");
},
and i call it in another page
<div>
<SelectCompound></SelectCompound>
</div>
now i want to get the method "selectedCompound" and recall it on this page
so i can access the ID of it to reload the page when the user select another name from the v-select
Props are passed down, Events are emited up. If you want to communicate directly between the parent and child, you pass props from parent to child, and the child reacts to the change in value. If you however want the parent to react to changes the child component, you need to emit events.
Here is an example.
Child
methods: {
selectedCompound(h2o) {
this.$emit('valChange', h2o)
},
}
Parent
<div>
<SelectCompound #valChange="handleChange"></SelectCompound>
</div>
methods: {
handleChange(h2o) {
// handle here
console.log('parent noticed change ' + h2o)
},
}
You can also use a bus (like Vuex) to have all components communicate to a separate state manager, but it increases the complexity quite a bit compared to simple even emit.
I made this jsfiddle for you, using the localStorage as persistence if u need to reload the page, and emitting a event when any option of the select is selected, this event triggered is called change on the select tag, then just you have emit to the parent the value selected.
And using the life cycle method created() of Vue to init the value from the persistence.
Related
I'm having trouble with vuex getters; On the first log in, with vuex in its initial state, actions which set some state properties are dispatched, however, subsequent usage of getters still retrieve null (the initial value for state properties)
I have the following in my vue component's script:
beforeCreate() {
store.dispatch('getSomething', 1).then(() => {
this.loading = false
})
},
computed: {
...mapGetters({
something: 'getSomething'
})
}
in the template:
<v-row v-if="!loading">
...
<span class="text-16">{{ something.name }}</span>
...
</v-row>
In the Something store:
const getters = {
getSomething: state => new Something(
state.something.id,
state.something.name,
state.something.description,
)
}
My expectation is that the action would be called before the component is loaded and being synchronous, the state should be filled by the said action which commits a mutation that sets the state.
Instead I get the following error which points to the getter:
TypeError: Cannot read properties of null (reading 'id')
at getSomething(something.js?62ce:12:1)
Update (mwe)
<template>
<v-row v-if="!loading">
<v-col class="mt-3" cols="12" lg="3">
<base-card>
<v-row>
<v-col class="text-center" cols="12">
<v-avatar>
<v-icon>mdi-liquid-spot</v-icon>
</v-avatar>
<div class="card-title ma-1 text-h5">{{ something.name }}</div>
<div class="d-flex align-center justify-center">
<span class="text-16">{{ something.description}}</span>
</div>
</v-col>
</v-row>
</base-card>
</v-col>
</v-row>
</template>
<script>
import store from "#/store";
import {mapGetters} from "vuex";
export default {
name: "Dashboard",
beforeCreate() {
// getSomething action
store.dispatch('getSomething', 2).then(() => {
this.loading = false
})
},
computed: {
...mapGetters({
// getSomething getter
something: 'getSomething'
})
},
methods: {},
data() {
return {
loading: true
}
}
}
</script>
<style scoped>
</style>
Your problem has (most likely) nothing to do with vuex.
This is probably a case of replacing a component with another component of the same type. When that happens, unless the component is key-ed using a unique primitive identifier, the component instance is reused (by Vue), so beforeCreate is only called once.
After that, whenever the component is updated, the beforeCreate hook is no longer called, (only beforeUpdate and updated hooks are called).
You either key the child component using a unique primitive identifier (perhaps something.id!?). Or you use beforeUpdated hook.
Another important aspect is the store action is asynchronous. Do not expect the creation of the component or its mounting to be waiting for the action to resolve. If that's what you want, you should call the action from parent component and condition the rendering of the child component (using v-if) on something that gets set when the action has resolved (and which gets unset when you dispatch the action again, to get another "something").
If my answer doesn't help you, consider creating a runnable Minimal, Reproducible Example. What you posted so far is not enough to create one and test potential solutions.
This makes your question unanswerable, renders it useless for future users having a similar problem and will likely result in the question being closed as off-topic.
I have a page with a stepper. I conditionally display a step, based on what step user is on. When flicks between steps (user can go back and forth) I noticed that my props are no longer passed over. What can be done about it?
View page:
<v-container v-if="stepNumber === 1">
<sectionOne :restrictions="restrictions" />
</v-container>
<v-container v-if="stepNumber === 2">
<sectionTwo :restrictions="restrictions" />
</v-container>
Within a section component:
#Prop() readonly restrictions: RestrictionsDomain = new RestrictionsDomain([]);
I understand that I can avoid a need for passing a prop with help of vuex, but I am wondering if anything can be done to get my props to work as is? Also, I can't use v-show for this either.
Since v-if destroys the element, thus removing the props' reactivity, and if you can't use v-show, I'd consider using a watcher on the prop and setting another reactive variable that'd trigger the visibility of that element.
So you'd get something like this:
<v-container v-if="step === 1">
<sectionOne :restrictions="restrictions" />
</v-container>
<v-container v-if="step === 2">
<sectionTwo :restrictions="restrictions" />
</v-container>
props: ['stepNumber'],
data() {
return {
step: 1,
}
},
watch: {
stepNumber(newVal) {
this.step = newVal
}
}
This is my first Vue app so go easy on the code :P
I have a list of users on one side and a component to edit those user details on the other. When selecting a user you see their details and can then click 'edit details'. I want to hide the edit details and show the user details when a new user is selected. Inside the component, I have a ShowEdit variable that is true or false and will either show or hide the edit area. I am sending a prop from the parent into this component when a new user is selected to hide the edit if it is open. I feel I am close as it is currently working perfect but I would like to get rid of the error
"Avoid mutating a prop directly since the value will be overwritten
whenever the parent component re-renders...."
Here are the important bits:
Home
<transition name="component-fade" mode="out-in">
<sidebar-client-content :theClient="selectedUser" :showEditHP="showEditHP"></sidebar-client-content>
</transition>
activate: function(el) {
this.showEditHP = true // Hide edit if new user selected
},
Component
<div id="sidebar-client-content">
<div class="staff_header">
<a v-if="showEdit" v-on:click="showEdit = !showEdit"><i class="fal fa-edit"></i>Edit</a>
<a v-if="!showEdit" v-on:click="showEdit = !showEdit"><i class="far fa-times"></i>Close</a>
</div>
<transition-group name="fadeHeight" mode="out-in">
<div class="client_information" v-if="showEdit">
<!-- The Client Details -->
</div>
<div class="client_information" v-if="!showEdit">
<!-- Client Edit Form -->
</div>
</transition-group>
</div>
export default {
props: [
'showEditHP', // Close edit if new user selected
],
computed: {
showEdit: {
get: function() {
return this.showEditHP
},
set: function (newValue) {
this.showEditHP = newValue
}
}
},
I understand the this.showEditHP = newValue line is where I make the edit but I can't seem to get it to work any other way. I want the parent to be able to overwrite it as the error says. Is there a way to achieve this and have the error removed?
Thanks.
As Nguyun said you can use $emit to send the value back to your parent to keep the values in sync. You can only change your parent data with emit otherwise it will just remain either true or false while the child continues to make it's changes. Keep the values in sync with emit and then use watch to check if the parent makes it's change.
<transition name="component-fade" mode="out-in">
<sidebar-client-content #clicked="onClickChild" :theClient="selectedUser" :showEditHP="showEditHP"></sidebar-client-content>
</transition>
onClickChild (value) {
// I am the child value inside the parent!
},
Then in the child
<div class="staff_header">
<a v-if="showEdit" v-on:click="showEdit = !showEdit; $emit('clicked', showEdit)"><i class="fal fa-edit"></i>Edit</a>
<a v-if="!showEdit" v-on:click="showEdit = !showEdit; $emit('clicked', showEdit)"><i class="far fa-times"></i>Close</a></i>Close</a>
</div>
The warning is really clear
In your child component, you're trying to mutate the value of showEditHP:
set: function (newValue) {
this.showEditHP = newValue
}
However, this kind of mutating is discouraged. Instead, we should emit an event from the child component and then let the parent component listen to that event and mutate the property itself.
In other words, the property showEditHP belongs to the parent component, well then the parent component should be the one which mutates it.
I'm trying to create a re-usable button. Is there any way to pass props and events down to the button itself without having to account for all the different possibilities?
For example, if I set a type on the <new-button> component, it passes the type to the <button> without me having to add a 'type' prop to the <new-button> component? Same goes for events, can a click event trigger from the button itself?
The component template itself is fairly simple and the button is the root component:
<template>
<button :disabled="submitting">
<i class="fa fa-spinner fa-spin" v-show="submitting"></i>
<i v-if="icon" :class="icon" v-show="!submitting"></i>
<slot></slot>
</button>
</template>
I do know that the class attribute passes through, but I don't see any way of passing through anything else.
How to pass all props down
If you want to pass all props down to an element without having to specify each of the props in particular, the way to achieve it is to pass the $props object (containing all the props passed to the component) using v-bind.
parent:
<custom-button :prop1="alice" :prop2="has" :prop3="a" :prop4="cat">
child:
<button v-bind="$props">
export default {
props: ['prop1', 'prop2', 'prop3', 'prop4']
}
How to prevent declaring all props
As you see, we still have to declare all the props in the props option of the component though. Luckily there's a workaround for that. Let's just pass an object of props:
parent:
<custom-button :props="{ prop1, prop2, prop3, prop4 }">
child:
<button v-bind="props">
export default {
props: ['props']
}
How to listen to native events
Finally for emitting events, you can register listeners for native Javascript events, such as click or input which bubble from HTML elements.
parent:
<custom-button #click.native="doStuff">
child:
<button>
I have a very small app that has a donation form. The form walks the user through the steps of filling in information. I have a main component, which is the form wrapper and the main Vue instance which holds all of the form data (model). All of the child components are steps within the donation process. Each child component has input fields that are to be filled out and those field will update the parent model so that I have all of the form data in the parent model when I submit the form. Here is how the components are put together:
<donation-form></donation-form> // Main/Parent component
Inside the donation-form component:
<template>
<form action="/" id="give">
<div id="inner-form-wrapper" :class="sliderClass">
<step1></step1>
<step2></step2>
<step3></step3>
</div>
<nav-buttons></nav-buttons>
</form>
</template>
Right now, I am setting the data from the inputs in each child component and then I have a watch method that is watching for fields to update and then I am pushing them to the $root by doing this...
watch: {
amount() {
this.$root.donation.amount = this.amount;
}
}
The problem is that one of my steps I have a lot of fields and I seem to be writing some repetitive code. Also, I'm sure this is not the best way to do this.
I tried passing the data as a prop to my child components but it seems that I cannot change the props in my child component.
What would be a better way to update the root instance, or even a parent instance besides add a watch to every value in my child components?
More examples
Here is my step2.vue file - step2 vue file
Here is my donation-form.vue file - donation-form vue file
You can use custom events to send the data back.
To work with custom events, your data should be in the parent component, and pass down to children as props:
<step1 :someValue="value" />
and now you want to receive updated data from child, so add an event to it:
<step1 :someValue="value" #update="onStep1Update" />
your child components will emit the event and pass data as arguments:
this.$emit('update', newData)
the parent component:
methods: {
onStep1Update (newData) {
this.value = newData
}
}
Here is a simple example with custom events:
http://codepen.io/CodinCat/pen/QdKKBa?editors=1010
And if all the step1, step2 and step3 contain tons of fields and data, you can just encapsulate these data in child components (if the parent component doesn't care about these row data).
So each child has its own data and bind with <input />
<input v-model="data1" />
<input v-model="data2" />
But the same, you will send the result data back via events.
const result = this.data1 * 10 + this.data2 * 5
this.$emit('update', result)
(again, if your application becomes more and more complex, vuex will be the solution.
Personally I prefer having a generic function for updating the parent, when working with forms, instead of writing a method for every child. To illustrate – a bit condensed – like this in the parent:
<template lang="pug">
child-component(:field="form.name" fieldname="name" #update="sync")
</template>
<script>
export default {
methods: {
sync: function(args) {
this.form[args.field] = args.value
}
}
}
</script>
And in the child component:
<template lang="pug">
input(#input="refresh($event.target.value)")
</template>
<script>
export default {
props: ['field', 'fieldname'],
methods: {
refresh: function(value) {
this.$emit('update', {'value': value, 'field': this.fieldname});
}
}
}
</script>
For your case you can use v-model like following:
<form action="/" id="give">
<div id="inner-form-wrapper" :class="sliderClass">
<step1 v-model="step1Var"></step1>
<step2 v-model="step2Var"></step2>
<step3 v-model="step3Var"></step3>
</div>
<nav-buttons></nav-buttons>
</form>
v-model is essentially syntax sugar for updating data on user input events.
<input v-model="something">
is just syntactic sugar for:
<input v-bind:value="something" v-on:input="something = $event.target.value">
You can pass a prop : value in the child components, and on change of input field call following which will change the step1Var variable.
this.$emit('input', opt)
You can have a look at this answer where you can see implementation of such component where a variable is passed thouugh v-model.