I have the following markup in a parent component
....
<div class="container-fluid">
<claims :userID="userId"></claims>
<claimForm :claimID="claimId" v-if="isDistributor"></claimForm>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="row">
<display :claimID="claimId"></display>
...........
data(){ return { claimId: null } },
beforeMount(){
this.userId = this.isDistributor? this.currentUser.id: null
this.eventEmitter.$on('claim.details', function(id) {
this.claimId = id
})
}
Then in the child component known as <display>
, watch: {
.....
claimID: function(n) {
console.log('claim');
if(n == null) return false
this.getClaimById()
}
.....
when an event bus this.eventEmitter.$emit() is emitted the parent component data claimdId is updated however the child component's claimID property doesn't seem to be updated thus the "watch" is not triggered.
What would cause the property to not be updated?
As explained here:
HTML attributes are case-insensitive, so when using non-string templates, camelCased prop names need to use their kebab-case (hyphen-delimited) equivalents:
so your code should be:
<claimForm :claim-iD="claimId" v-if="isDistributor"></claimForm>
Related
Is there a vue event when a v-if condition ceases to be true?
For example if I want to set y when div id foo is removed for whatever reason.
What should go in place of #whenDivGoes?
<div v-if="z">
<div id="foo"
v-if="x"
#whenDivGoes="y = true"
>
Hello
</div>
<div>
<div v-if="!x">Not X</div>
<div v-if="y">Bye</div>
If I understand correctly you want to set value of 'y' to be true when the foo div cease to exist.
Your foo div is shown when your 'x' is true, and hidden when x is false, so you can declare 'y' as a computed property that depends on x.
computed:{
y(){
return !x;
}
}
then
<div v-if="z">
<div id="foo"
v-if="x">
Hello
</div>
<div>
<div v-if="!x">Not X</div>
<div v-if="y">Bye</div>
note: declaring 'y' as computed property would prevent you from modifying/assigning value to it directly. if you want to 'do something' when 'y' value is changed you can use watch on 'y'.
With v-if, is the node is taken directly from the virtual DOM, you would need to watch and create your own callback.
If you are looking for a quick solution similar to your example, i have written a quick directive to you put directly into the element you want a callback on, but it would require you to use v-show as apposed to v-if (as it only changes the css display class)
Simply change the div you want a callback on when its not longer displayed, and add v-show-event:
<div id="foo"
v-show="x"
v-show-event="() => y = true"
>
So basically what's happening, is the directive checks if the element has style.display set to none
Vue.directive('show-event', {
update: function(el, binding, vnode) {
if(el.style.display === "none") {
if(typeof binding.value==='function') {
binding.value.bind(vnode.context)(event)
}
}
}
});
new Vue({
el: "#app",
data: {
done: false,
newEv: "this is some text"
},
methods: {
toggle: function(){
this.done = !this.done
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2
v-show="!done"
v-show-event="() => newEv = 'show event was hidden'">
v-show-event directive
</h2>
<button #click="toggle()">
Toggle
</button>
<div v-text="newEv"></div>
</div>
<template>
<div class="card-deck mb-3 text-center">
<div class="card mb-3 box-shadow">
<div class="card-header">
Numbers Checked
</div>
<div class="card-body card-info color-accent" v-model="numbers_checked" v-text="numbers_checked">
</div>
</div>
</div>
</template>
<script>
export default {
props:
[
'overviewAnalytics',
],
data() {
return {
numbers_checked: this.overviewAnalytics.numbers_checked
};
},
created() {
this.channelTemperatureReading.listen('TemperatureReadingUpdate', reading => {
axios.get('/home/get-overview-analytics').then(resp => {
this.numbers_checked = 12; //resp.data.numbers_checked + 100;
});
});
},
computed: {
channelTemperatureReading() {
return window.Echo.channel('temperature-reading');
},
},
};
</script>
I've tried everything but text is not updating. Confirmed from every aspect that data does change.
Changes from AXIOS are coming just ok. I even tried to put custom value but no avail.
I don't what is issue here.
v-model only works on input, textarea, and select elements
You appear to be misusing computed properties which rely on reactive dependencies to execute however yours is wrapping window.Echo.channel('temperature-reading') which Vue knows nothing about.
I suggest you remove the computed property and use something like this
created() {
const channel = window.Echo.channel('temperature-reading')
channel.listen('TemperatureReadingUpdate', reading => {
axios.get('/home/get-overview-analytics').then(({ data }) => {
// console.log('get-overview-analytics', data.numbers_checked)
this.numbers_checked = data.numbers_checked + 100
})
})
}
As others have mentioned, v-model is not appropriate here so you should also remove that.
Don't use v-model with div, it's for inputs.
<div v-text="numbers_checked"></div>
From the documentation on v-model:
Usage: Create a two-way binding on a form input element or a
component.
I got a problem:
I included a custom component like that:
{{item.lists}}
<div v-for="(listitem, index) in item.lists" :key="index"
class="media align-items-center bg-white ui-bordered py-3 mb-2">
<div class="component-move ion ion-ios-move text-muted text-big p-4"></div>
<div class="media-body">
<div class="container mt-2">
<div class="row">
<viewlist :item="listitem" :name="`${listitem.id}-${index}`"/>
</div>
</div>
</div>
</div>
So now there's an edit component:
<editlistitem :name="item" :item="ListItem"/>
Inside this edit component, there's a watcher:
watch: {
'item.value': function (value) {
let vm = this;
_.each (vm.item.children, function (child) {
child.id === value ? child.value = true : child.value = false;
});
}
},
If the value of the listitem changes, the value of the child is set to false or true.
These changes are reflected in {{item.lists}} in my parent view. But
<viewlist :item="listitem" :name="`${listitem.id}-${index}`"/>
does not show the changes. It sticks to the old values.
It can be Vue reactivity problem.
You can try use Vue.set
_.each (vm.item.children, (child, index) => {
const newChild = {...child, value: child.id === value }
vm.$set(vm.item.children, index, newChild)
});
The better way is emitting an event to parent and change data in parent since Vue doesn't encourage to mutate props in children component.
//this code in vue js for accordion this is working but not show open only //one at the same time.
//there component and pass props data.
<packing-material-category v-for="(category, index) in packingMaterialCategories" :category="category" :key="index"></packing-material-category>
//there template to render data.
<template>
<div class="packing-categories">
<div :class="packingCategoryClass">
<h3 class="packing-category__title" #click="toggleAccordion(category.title)" v-text="category.title" />
<div v-show="accordionOpen===true" class="packing-category__content">
<div v-if="category.description" class="packing-category__description" v-text="category.description" />
</div>
</div>
</div>
//call method for open and close
toggleAccordion() {
this.accordionOpen = !this.accordionOpen;
}
You need to track which item is shown outside of packing-material-category component (value of openedIndex) and to pass that value down to components. When value changes in the packing-material-category component, you emit event change-accordion, and in your parent component you update value of openedIndex
Try this
In your parent component, add openedIndex: 0 to data.
If you want everything closed by default, set value to false.
Update template to pass down the props index and openedIndex, so that components know when to show/hide.
<packing-material-category
v-for="(category, index) in packingMaterialCategories"
:key="index"
:index="index"
:openedIndex="openedIndex"
:category="category"
#change-accordion="(newOpen) => openedIndex = newOpen"
>
</packing-material-category>
And the packing-material-category component could look like this
<template>
<div class="packing-categories">
<div :class="packingCategoryClass">
<h3 class="packing-category__title" #click="toggleAccordion" v-text="category.title" />
<div v-show="index === openedIndex" class="packing-category__content">
<div v-if="category.description" class="packing-category__description" v-text="category.description" />
</div>
</div>
</div>
</template>
<script>
export default {
props: [
'category',
'index', // index of this component
'openedIndex', // which item should be shown/opened
],
data() {
return {
packingCategoryClass: '',
}
},
methods: {
toggleAccordion() {
// Show current item. If already opened, hide current item
let value = this.index === this.openedIndex ? false : this.index;
// notify parent component about the change
this.$emit('change-accordion', value)
}
}
}
</script>
Here what appears in the console when I run my code:"Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "isChecked"".
I have seen what other posts say about it but i can't adapt it on my problem.
Could someone explain it to me please?
PARENT:
template:
<div class="checkBox-container">
<input type="checkbox"/>
<!-- <div class="check" :class="[size]" v-if="!isChecked"></div>
<div class="check" :class="[size]" v-else>
<div>v</div>
</div> -->
<div class="check" :class="[size]" #click="changeVal">
<div v-if="isChecked">v</div>
</div>
<div><label class="label">{{label}}</label></div>
<div><span class="subLabel">{{subLabel}}</span></div>
script:
export default {
name: "ax-checkbox",
props: {
label: String,
subLabel: String,
size: String,
isChecked: false,
checks: []
},
methods: {
changeVal() {
this.isChecked = !this.isChecked;
this.$emit("changeVal");
}
}
};
CHILD
<div class="filters">
<ax-checkbox label="Où :" subLabel="Ville" size="small"></ax-checkbox>
<div class="separator"></div>
<ax-checkbox label="Quoi :" subLabel="Thématique(s)" size="small"></ax-checkbox>
<div class="separator"></div>
<ax-checkbox label="Quand :" subLabel="Dans..." size="small"></ax-checkbox>
</div>
The prop you are mutating is isChecked.
So, create a local data variable (initialized it with isChecked) and mutate it instead:
export default {
name: "ax-checkbox",
props: {
label: String,
subLabel: String,
size: String,
isChecked: false,
checks: []
},
data() {
return {isCheckedInternal: this.isChecked}
},
methods: {
changeVal() {
this.isCheckedInternal = !this.isCheckedInternal;
this.$emit("changeVal");
}
}
};
And replace it in the template:
<div class="checkBox-container">
<input type="checkbox"/>
<!-- <div class="check" :class="[size]" v-if="!isCheckedInternal"></div>
<div class="check" :class="[size]" v-else>
<div>v</div>
</div> -->
<div class="check" :class="[size]" #click="changeVal">
<div v-if="isCheckedInternal">v</div>
</div>
<div><label class="label">{{label}}</label></div>
<div><span class="subLabel">{{subLabel}}</span></div>
Note: The code above will use the prop isChecked only as initializer. If the parent changes in any way the value it passed to isChecked, the child component will not pick that change up. If you want to pick it up, add a watch in addition to the proposed code above:
//...
watch: {
isChecked(newIsChecked) {
this.isCheckedInternal = newIsChecked;
}
}
};
Ideal
There are some possible improvements to your code. Here are some suggestions:
subLabel prop should be sub-label
instead of emitting a changeVal value, emit update:isChecked and then you can use :is-checked.sync="myCheckedValue" in the parent.
This way you can still bind internally to the prop isChecked and not change it, but emit events and react to when the parent changes isChecked instead.
If you wanted to go the extra mile (and think it is worth it), you could also add a model option to your component, so you can be able to use v-model instead of :is-checked.sync.
See demo below.
Vue.component("ax-checkbox", {
template: '#axCheckboxTemplate',
props: {
label: String,
subLabel: String,
size: String,
isChecked: false,
checks: []
},
model: { // <== this part to will also enable v-model besides :is-checked.async
prop: 'isChecked',
event: 'update:isChecked'
},
methods: {
updateIsChecked() {
this.$emit("update:isChecked", !this.isChecked);
}
}
})
new Vue({
el: '#app',
data: {
myCheckedValueSync: false, myCheckedValueVModel: false,
}
});
<script src="https://unpkg.com/vue#2.5.13/dist/vue.js"></script>
<template id="axCheckboxTemplate">
<div class="checkBox-container">
<input type="checkbox" :checked="isChecked" #change="updateIsChecked" />
<div class="check" :class="[size]" #click="updateIsChecked">
CLICK ME<div v-if="isChecked">v</div>
</div>
<label class="label">{{label}}</label><span class="subLabel">{{subLabel}}</span>
</div>
</template>
<div id="app">
<div class="filters">
<pre>parent's myCheckedValueSync: {{ myCheckedValueSync }}</pre>
<ax-checkbox label="Où :" sub-label="Ville" size="small" :is-checked.sync="myCheckedValueSync">
</ax-checkbox>
<pre>parent's myCheckedValueVModel: {{ myCheckedValueVModel }}</pre>
<ax-checkbox label="Quoi :" sub-label="Thématique(s)" size="small" v-model="myCheckedValueVModel">
</ax-checkbox>
</div>
</div>
As it appears you're trying to update the value of a property passed into your component, your component is in fact a custom input component. Take a look at the excellent Vue docs on this topic. I've summarised the idea below.
2-way databinding in Vue is handled by v-model. Applied to your isChecked property this comes down to v-model="isChecked", which is in fact syntactic sugar for :value="isChecked" #input="evt => isChecked = evt.target.value".
Thus for your component, you need to do the following:
Update the name of the isChecked property to value
In your changeVal method, emit an input event, like:
changeVal() { this.$emit("input", !this.value); }
If need be, you could still also emit the changeVal event.