Hi all and thank for helping :)
Context :
In my child i want pass a boolean to the parent for hidden a component in the parent if user clicked
in my child component ( name : connexionDesktop ) :
<button v-on:click="$emit(childMessage)"> Start </button>
data () {
return {
childMessage: true
}
}
in the parent i try :
<connexionDesktop v-if="fromChild == false " v-on:childToParent="childMessage"> </connexionDesktop>
data () {
fromChild:false
}
methods: {
childMessage (value) {
alert('from child' + value );
this.fromChild = value
}
}
but that's not working ... i think i am a noob :D i can't send a message from child to parent ^^""
can you help me ? thank a lot
The first argument for the $emit method should be the event name. So your code should look better like this:
// child component
<template>
<button v-on:click="handleClick"> Start </button>
</template>
<script>
export default {
data () {
return {
childMessage: true
}
},
methods: {
handleClick() {
this.$emit('update:parent', this.childMessage)
}
}
}
</script>
Then in the parent component, you listen for the event and pass in the value the child emitted to a local value of the parent like so:
<template>
<connexionDesktop v-if="fromChild == false" #update:parent="fromChild = $event">
</connexionDesktop>
</template>
<script>
export default {
data () {
return {
fromChild: false
}
},
}
</script>
As it's stated in the docs on $emit here, the first parameter is the custom event name.
You can do something like this:
<button v-on:click="$parent.$emit('childToParent', childMessage)"> Start </button>
data () {
return {
childMessage: true
}
}
and in the parent:
<connexionDesktop v-if="fromChild == false " v-on:child-to-parent="childMessage"> </connexionDesktop>
...
data () {
fromChild:false
}
methods: {
childMessage (value) {
alert('from child' + value );
this.fromChild = value
}
}
...
Related
I am trying to pass the radio button value to its parent
here is the example:
Child
<template>
<radio v-model="type" value="TypeOne" #change="TypeOne()"></el-radio>
<radio v-model="type" value="TypeTwo" #change="TypeTwo()"></el-radio>
</template>
<script>
export default {
methods: {
TypeOne() {
this.$emit('input', 'TypeOne');
},
TypeTwo() {
this.$emit('input', 'TypeTwo');
}
}
}
</script>
parent
<template>
<component #input="onTypeChange"></component>
<div v-show="type"></div>
<div v-show="!type"></div>
</template>
<script>
export default {
data() {
return {
type: 'true',
};
},
methods: {
onTypeChange () {
var TypeOne = false
var TypeTwo = false
if (TypeOne == TypeOne) {
this.type = false;
} else if (TypeTwo == TypeTwo) {
this.type = true;
} else {
console.log('typeChanged');
return false;
}
}
}
}
</script>
I am trying to use radio data to hide or show some element in the parent component. in initial selection it works but after radio selection is changed it does not update the parent. How can I make the radio data reactive in my case?
You passed a parameter when you called this.$emit('input', 'TypeOne');
but the method emitted is not accepting a parameter.
Add a parameter on onTypeChange() and use the parameter on your logic
onTypeChange (type) {
if (type == 'TypeOne') {
this.type = false;
} else if (type == 'TypeTwo') {
this.type = true;
} else {
console.log('typeChanged');
return false;
}
}
I have this vue component:
<template>
<div id="OrderTypeSelect" class="order-type-select">
<b-form-select v-model="curDocType" :options="options" class="mb-3">
</b-form-select>
</div>
</template>
the value of the select input is bound to the Vuex store like this:
computed: {
curDocType: {
get () {
return this.$store.state.curDocType
},
set (value) {
this.$store.commit('setcurDocType', value)
}
}
}
What I can't figure out is how to conditionally prevent the select value from changing. I've tried this:
computed: {
curDocType: {
get () {
return this.$store.state.curDocType
},
set (value) {
if (this.$store.state.curOrder == ""){
this.$store.commit('setcurDocType', value)
}
else{
console.log("Debe descartar");
this.curDocType.get() //This throws error!
}
}
}
}
Even if I don't commit the value to the store, the value in the input field is changed.
I need to call get() again (or something else) to make this binding persistent when my condition is triggered:
if (this.$store.state.curOrder != "") {
//Do not modify store and return the input selection to it's previous value
}
In your case i recommend to use a data object item called curDocType and watch it instead of using computed property :
<b-form-select v-model="curDocType" :options="options" class="mb-3">
data object :
data(){
return{
curDocType:this.$store.state.curDocType
};
}
watch property :
watch:{
curDocType(newval,oldval){
if (this.$store.state.curOrder == ""){
this.$store.commit('setcurDocType', newval)
}else{
this.$nextTick(() => {this.curDocType=this.$store.state.curDocType})
}
}
}
Try <b-form-select v-model="curValue" #input="setValue" :options="options" class="mb-3">
Where curValue is a variable in data and setValue is a method:
methods: {
setValue(value) {
if (this.$store.state.curOrder == "") {
this.$store.commit('setcurDocType', value)
this.curValue = value
} else {
console.log("Debe descartar");
this.$nextTick(() => {this.curValue = this.$store.state.curDocType})
}
}
}
I select the currency in the parent component of Vue using bootstrap select:
<template>
...
<div class = "dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button"> {{currency}} </ button>
<div class = "dropdown-menu">
<button class = "dropdown-item" # click = "currencyChange ('USD')"> USD </ button>
<button class = "dropdown-item" # click = "currencyChange ('EUR')"> EUR </ button>
</div>
</div>
...
<div class = "box">
<box v-bind: currency-name = 'currency' />
</div>
<template>
<script>
...
data () {
return {
currency: 'USD'
}
},
components: {
box: component
},
methods: {
currencyChange (currency) {
this.currency = currency;
}
}
...
</script>
In the child component "box" I get data from server via the axios.get call and render it:
<script>
...
props: ['currencyName'],
data () {
return {
boxData: {},
}
},
created () {
axios.get (URL + this.currencyName)
.then (response => {
this.Data = response.data;
})
.catch (e => {
this.errors.push (e)
})
},
...
</script>
The problem is that if the EUR currency is selected, I never send new query to the server and data in the box component remains the same, for the 'USD' currency except currencyChange variable. If rename "created" hook to "updated" in component, everything starts to work as it should, with one exception - there are constant calls to the server.
How can I fix this wrong behaviour into a single call to the server only after dropdown click?
You can use the watch property to achieve this.
<script>
...
props: ['currencyName'],
data () {
return {
currentCurrency: this.currencyName,
boxData: {},
}
},
watch: {
currentCurrency() {
this.getData();
}
},
methods: {
getData() {
axios.get (URL + this.currentCurrency)
.then (response => {
this.Data = response.data;
})
.catch (e => {
this.errors.push (e)
})
},
}
...
</script>
Take a look at https://v2.vuejs.org/v2/guide/computed.html#Watchers
EDIT
As D F suggest you can add immediate: true, to your watcher to trigger it at the component initialization
I have this 3 components in VueJS. The problem i want to solve is: When i click at vehicle component, it needs to be selected (selected = true) and other vehicles unselected.
What i need to do for two-way data binding? Because i'm changing this selected property in VehiclesList.vue component and it also need to be changed in Monit.vue (which is a parent) and 'Vehicle.vue' need to watch this property for change class.
Also problem is with updating vehicles. In Monit.vue i do not update full object like this.vehicles = response.vehicles, but i do each by each one, and changing only monit property.
Maybe easier would be use a store for this. But i want to do this in components.
EDITED:Data sctructure
{
"m":[
{
"id":"v19",
"regno":"ATECH DOBLO",
"dt":"2017-10-09 13:19:01",
"lon":17.96442604,
"lat":50.66988373,
"v":0,
"th":0,
"r":0,
"g":28,
"s":"3",
"pow":1
},
{
"id":"v20",
"regno":"ATECH DUCATO_2",
"dt":"2017-10-10 01:00:03",
"lon":17.96442604,
"lat":50.6698494,
"v":0,
"th":0,
"r":0,
"g":20,
"s":"3"
},
]
}
Monit.vue
<template>
<div class="module-container">
<div class="module-container-widgets">
<vehicles-list :vehicles="vehicles"></vehicles-list>
</div>
</div>
</template>
<script>
import VehiclesList from '#/components/modules/monit/VehiclesList.vue';
export default {
name: "Monit",
data (){
return {
vehicles: null
}
},
components: {
VehiclesList
},
methods: {
getMonitData(opt){
let self = this;
if (this.getMonitDataTimer) clearTimeout(this.getMonitDataTimer);
this.axios({
url:'/monit',
})
.then(res => {
let data = res.data;
console.log(data);
if (!data.err){
self.updateVehicles(data.m);
}
self.getMonitDataTimer = setTimeout(()=>{
self.getMonitData();
}, self.getMonitDataDelay);
})
.catch(error => {
})
},
updateVehicles(data){
let self = this;
if (!this.vehicles){
this.vehicles = {};
data.forEach((v,id) => {
self.vehicles[v.id] = {
monit: v,
no: Object.keys(self.vehicles).length + 1
}
});
} else {
data.forEach((v,id) => {
if (self.vehicles[v.id]) {
self.vehicles[v.id].monit = v;
} else {
self.vehicles[v.id] = {
monit: v,
no: Object.keys(self.vehicles).length + 1
}
}
});
}
},
},
mounted: function(){
this.getMonitData();
}
};
</script>
VehiclesList.vue
<template>
<div class="vehicles-list" :class="{'vehicles-list--short': isShort}">
<ul>
<vehicle
v-for="v in vehicles"
:key="v.id"
:data="v"
#click.native="select(v)"
></vehicle>
</ul>
</div>
</template>
<script>
import Vehicle from '#/components/modules/monit/VehiclesListItem.vue';
export default {
data: function(){
return {
isShort: true
}
},
props:{
vehicles: {}
},
methods:{
select(vehicle){
let id = vehicle.monit.id;
console.log("Select vehicle: " + id);
_.forEach((v, id) => {
v.selected = false;
});
this.vehicles[id].selected = true;
}
},
components:{
Vehicle
}
}
</script>
Vehicle.vue
<template>
<li class="vehicle" :id="data.id" :class="classes">
<div class="vehicle-info">
<div class="vehicle-info--regno font-weight-bold"><span class="vehicle-info--no">{{data.no}}.</span> {{ data.monit.regno }}</div>
</div>
<div class="vehicle-stats">
<div v-if="data.monit.v !== 'undefined'" class="vehicle-stat--speed" data-name="speed"><i class="mdi mdi-speedometer"></i>{{ data.monit.v }} km/h</div>
</div>
</li>
</template>
<script>
export default {
props:{
data: Object
},
computed:{
classes (){
return {
'vehicle--selected': this.data.selected
}
}
}
}
</script>
Two-way component data binding was deprecated in VueJS 2.0 for a more event-driven model: https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow
This means, that changes made in the parent are still propagated to the child component (one-way). Changes you make inside the child component need to be explicitly send back to the parent via custom events: https://v2.vuejs.org/v2/guide/components.html#Custom-Events or in 2.3.0+ the sync keyword: https://v2.vuejs.org/v2/guide/components.html#sync-Modifier
EDIT Alternative (maybe better) approach:
Monit.vue:
<template>
<div class="module-container">
<div class="module-container-widgets">
<vehicles-list :vehicles="vehicles" v-on:vehicleSelected="onVehicleSelected"></vehicles-list>
</div>
</div>
</template>
<script>
import VehiclesList from '#/components/modules/monit/VehiclesList.vue';
export default {
name: "Monit",
data (){
return {
vehicles: null
}
},
components: {
VehiclesList
},
methods: {
onVehicleSelected: function (id) {
_.forEach((v, id) => {
v.selected = false;
});
this.vehicles[id].selected = true;
}
...other methods
},
mounted: function(){
this.getMonitData();
}
};
</script>
VehicleList.vue:
methods:{
select(vehicle){
this.$emit('vehicleSelected', vehicle.monit.id)
}
},
Original post:
For your example this would probably mean that you need to emit changes inside the select method and you need to use some sort of mutable object inside the VehicleList.vue:
export default {
data: function(){
return {
isShort: true,
mutableVehicles: {}
}
},
props:{
vehicles: {}
},
methods:{
select(vehicle){
let id = vehicle.monit.id;
console.log("Select vehicle: " + id);
_.forEach((v, id) => {
v.selected = false;
});
this.mutableVehicles[id].selected = true;
this.$emit('update:vehicles', this.mutableVehicles);
},
vehilcesLoaded () {
// Call this function from the parent once the data was loaded from the api.
// This ensures that we don't overwrite the child data with data from the parent when something changes.
// But still have the up-to-date data from the api
this.mutableVehilces = this.vehicles
}
},
components:{
Vehicle
}
}
Monit.vue
<template>
<div class="module-container">
<div class="module-container-widgets">
<vehicles-list :vehicles.sync="vehicles"></vehicles-list>
</div>
</div>
</template>
<script>
You still should maybe think more about responsibilities. Shouldn't the VehicleList.vue component be responsible for loading and managing the vehicles? This probably would make thinks a bit easier.
EDIT 2:
Try to $set the inner object and see if this helps:
self.$set(self.vehicles, v.id, {
monit: v,
no: Object.keys(self.vehicles).length + 1,
selected: false
});
Possible to change onclick function like changing props, like changing 'props message' to 'new message' ?
For example:
var SmallMessageBox = React.createClass({
getDefaultProps: function() {
return {
message: 'props message',
onClick: 'this.eventHandler_Two'
}
},
eventHandler_One: function(){
console.log('event1');
},
eventHandler_Two: function(){
console.log('event2');
},
render: function(){
return (
<div>
<small>{this.props.message}</small>
<button onClick={this.eventHandler_One}>button</button>
</div>
);
}
});
React.render(
<SmallMessageBox message="new message" onClick="new onClick function for event2" />, document.getElementById('react-container'),
function(){
console.log('after render');
}
);
Yes, components can be supplied with properties of type function. You can either bind event handlers directly to functions passed through props or do something in your internal component method, before executing the prop function. Please note, that you cannot change definition of supplied function, once target component was initialized, it will not be updated.
Also, in many cases you must use bind on your passed in function to maintain proper context.
Here's how it should look like in accordance with your example:
var SmallMessageBox = React.createClass({
propTypes: {
message: React.PropTypes.string.isRequired,
onClick: React.PropTypes.func
},
getDefaultProps: function() {
return {
message: 'props message',
onClick: function() {
console.log("I will be executed, only if props.onClick was not specified");
}
}
},
eventHandler: function() {
},
render: function() {
return (
<div>
<small>{this.props.message}</small>
<button onClick={ this.props.onClick }>button</button>
</div>
);
}
});
React.render(
<SmallMessageBox onClick={function() { console.log( "remove me to get the other console.log"); }} message="new message"/>, document.getElementById('react-container'),
function(){
console.log('after render');
}
);
I would also encourage you to implicitly specify your props with their type. You can find more information here.