I have a component and i am passing value 543 to props :prop-room-selected,
<navigation-form :prop-room-selected='543'>
</navigation-form>
Now, From a button click, i am calling the function updateCoachStatus to change the value of propRoomSelected, but the props value is not updating.
{
template: '#navigation-form',
props: ['propRoomSelected'],
data: function () {
return {
roomSelected: this.propRoomSelected,
}
},
methods:{
updateCoachStatus: function(event){
this.propRoomSelected = 67;
}
}
}
I dont know how to change the value of props from function. Is it possible in Vue to update the value of props??
What you are doing will throw a warning in Vue (in the console).
[Vue warn]: 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: "propRoomSelected"
The value will actually change inside the component, but not outside the component. The value of a parent property cannot be changed inside a component, and, in fact, the updated value will be lost if the parent re-renders for any reason.
To update the parent property, what you should do is $emit the updated value and listen for the change in the parent.
Vue.component("navigation-form",{
template: '#navigation-form',
props: ['propRoomSelected'],
data: function () {
return {
roomSelected: this.propRoomSelected,
}
},
methods:{
updateCoachStatus: function(event){
this.$emit("update-room-selected", 67) ;
}
}
})
And in your parent template listen for the event
<navigation-form :prop-room-selected='propRoomSelected'
#update-room-selected="onUpdatePropRoomSelected">
</navigation-form>
Here is an example.
This is a common pattern and Vue implemented a directive to make it slightly easier called v-model. Here is a component that supports v-model that will do the same thing.
Vue.component("navigation-form-two",{
template: '#navigation-form-two',
props: ['value'],
data: function () {
return {
roomSelected: this.value,
}
},
methods:{
updateCoachStatus: function(event){
this.$emit("input", 67) ;
}
}
})
And in the parent template
<navigation-form-two v-model="secondRoomSelected">
</navigation-form-two>
Essentially, for your component to support v-model you should accept a value property and $emit the input event. The example linked above also shows that working.
Another approach is using a computed property for handling props:
{
template: '#navigation-form',
props: ['propRoomSelected'],
data () {
return {
roomSelected: this.computedRoomSelected,
changeableRoomSelected: undefined
}
},
computed: {
computedRoomSelected () {
if (this.changeableRoomSelected !== undefined) {
return this.changeableRoomSelected
}
return this.propRoomSelected
}
},
methods: {
updateCoachStatus (event) {
this.changeableRoomSelected = 67
}
}
}
Related
I've a hard time in understanding the methods of vue. In my put-request users can edit, delete images. In parent component the get-request loads the images and the are pushed to an image-gallery (the child-component) via properties. In my set up the console.log is always empty.
//PARENT COMPONENT
<template>
<div class="form-group">
<image-gallery :serverData="serverMap"/>
</div>
</template>
<script>
import ImageGallery from './ImageGallery.vue';
export default {
components:{ImageGallery},
data: () => ({
serverMap: {
title: '',
file: ''
}
}),
mounted () {
//AJAX ETC get servermap
.then((response) => {
this.serverMap = response.data
})
}
Just a normal straight parent-child situation. Here under the child-component
<template>
</template>
<script>
export default {
name: 'ImageGallery',
//incoming data
props: {
serverData: {
type: Object,
default () {
return {
hasLabels: true,
isHorizontal: false
}
}
}
},
created: function () {
this.loadImages()
},
methods: {
loadImages () {
console.log(this.serverData.file)
//do something with the serverData
//prepare for fileReader function
//together with new image validation
}
}
The method 'loadImages' should be automatically delevering the serverData via computed.But is doesn t. Who can help?
There is race condition.
Either not render a child until data is available; serverMap needs to be null instead of empty object in order to be distinguished from populated object:
<image-gallery v-if="serverMap" :serverData="serverMap"/>
Or delay data access in a child until it's available instead of doing this immediately in created:
watch: {
serverData(data) {
if (data)
this.loadImages()
}
}
I am using Laravel 7 and Vue.js 2.
I want to pass an object of validation errors from the catch block of an axios call to a parent component but for some reasons it doesn't work.
This is the code of the axios call:
runReport: function() {
let self = this;
const url = "api/get_report?room="+this.formReport['room']+"&participant="+this.formReport['participant']+"&start="+this.formReport['start']+"&end="+this.formReport['end'];
axios.get(url)
.then((response) => {
console.log(response.data.data);
this.meetingsReport = response.data.data;
this.$emit('passMeetings', this.meetingsReport);
this.$emit('success');
this.errors = {};
})
.catch(function(error) {
console.log(error.response.data);
self.errors = error.response.data;
alert(self.errors);
self.$emit('failure');
self.$emit('passErrors', self.errors); //problem
console.log('call ended');
});
}
This is the code in the parent component:
<template>
<div>
<report-meeting #passMeetings="onPassMeetings" #failure="displayTable=false" #success="displayTable=true"></report-meeting>
<hr>
<validated-errors :errorsMeeting="errorsMeeting" #passErrors="onPassErrors" v-if="displayTable===false"></validated-errors>
<table-report :meetingsSelected="meetingsSelected" v-if="displayTable===true"></table-report>
</div>
</template>
<script>
import TableReport from "./TableReport.vue"
import ReportMeeting from "./ReportMeeting.vue"
import ValidatedErrors from "./ValidatedErrors.vue"
export default {
components: {
'table-report': TableReport,
'report-meeting': ReportMeeting,
'validated-errors': ValidatedErrors
},
mounted() {
console.log('Component mounted.');
},
data: function() {
return {
displayTable: false,
meetingsSelected: {},
errorsMeeting: {}
}
},
methods: {
onPassMeetings(value) {
console.log(value);
this.meetingsSelected = value;
},
onPassErrors(value) {
console.log('errors passed'); //never used
this.errorsMeeting = value;
}
}
}
</script>
In the console I visualize no errors (except an 422 Unprocessable Entity). The strange thing is that the first emit works (failure), but the second one doesn't work (passErrors).
In the parent function onPassErrors I put a console.log that is never used so I suppose that the function is never called.
Can help?
This is likely caused by an event name mismatch, which can occur when using in-DOM templates because HTML attributes are automatically lower-cased (#passErrors becomes #passerrors in the DOM).
When using the development build of Vue, you'd see a warning in the browser's console:
[Vue tip]: Event "passerrors" is emitted in component but the handler is registered for "passErrors". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "pass-errors" instead of "passErrors".
This is not a problem in single file components (demo 1) or string templates (demo 2), but if you must stick with in-DOM templates, custom event names are recommended to be kebab-case:
<!-- Parent.vue -->
<MyComponent #pass-errors="onPassEvent" />
// MyComponent.vue
runReport() {
this.$emit('pass-errors', /*...*/)
}
demo 3
Parent Component
<template>
<ChildComponent #send-message="handleSendMessage" />
</template>
Parent Component Script
export default {
methods: {
handleSendMessage(event, value) {
console.log('From the child:', value);
}
}
}
child Component
export default {
props: {
method: { type: Function },
},
computed:{
rep_9100_3() {
return parseInt(this.rep_6000_3) - this.decutable_allowance
},
},
mounted() {
this.$emit('send-message', this.rep_9100_3);
}
}
Yes you simply need to change handleSendMessage(event, value) to handleSendMessage(value).
One extra thing to note. It's could practice to pass a radix value when using parseInt:
parseInt(this.rep_6000_3, 10)
I may be wrong in my understanding of props but I can't seem to be able to pass a prop to a component and then use the value in a method?
So far I am able to get data from a fixed API and output everything from the vue component, now I would like the api source to be dependent on the variable passed to the component.
My blade template:
<projectstatuses :userslug="this-user" :projectslug="this-project"></projectstatuses>
Then in my Vue Component:
export default {
props: {
userslug: {
type: String,
default: "other-user",
},
projectslug: {
type: String,
default: "other-project",
}
},
data() {
return {
statuses : [],
}
},
created(){
this.getStatuses();
},
methods : {
getStatuses(){
console.log(this.userslug);
console.log(this.projectslug);
axios.get('/api/' + this.userslug + '/' + this.projectslug)
.then((response) => {
let statuses = response.data;
this.statuses = statuses.statuses;
console.log(response.data.statuses);
})
.catch(
(response) => {
console.log('error');
console.log(response.data);
}
);
}
}
}
In the console I get the default values, if I remove the default values I get undefined. I have tried removing the api method and simply console logging the values but I still get undefined. Is what I'm trying to do possible or have I completely misunderstood how props work?
You are trying to bind this-user and this-project as a properties not as values ,
So you will need to define them in the data object in the parent,
but if you want to pass this-user and this-project just as value remove the : try that:
<projectstatuses userslug="this-user" projectslug="this-project"></projectstatuses>
Dynamic-Props
Don't add : in your template:
<projectstatuses userslug="this-user" projectslug="this-project"></projectstatuses>
Vue will expect there's data bound to this-user.
Problem: Although from the Vue DevTools I am passing the prop correctly and the router-view component has access to the data that it needs and in the correct format, whenever I try to access any of the data properties from within the template I get Uncaught TypeError: Cannot read property 'name' of null. It's really confusing because from the DevTools everything is a valid object and the properties are not null.
App.js
const game = new Vue({
el: '#game',
data: function() {
return {
meta: null,
empire: null,
planets: null
};
},
created: () => {
axios.get('/api/game').then(function (response) {
game.meta = response.data.meta;
game.empire = response.data.empire;
game.planets = response.data.planets;
});
},
router // router is in separate file but nothing special
});
main.blade.php
<router-view :meta="meta" :empire="empire" :planets="planets"></router-view>
script section of my Component.vue file
export default {
data: function() {
return {
}
},
props: {
meta: {
type: Object
},
empire: {
type: Object
},
planets: {
type: Array
}
}
}
Any ideas? Thanks in advance.
Because of your data is async loading so when my Component.vue renders your data in parent component may not be there. So you need to check if your data is loaded. You can try this code:
{{ meta != null && meta.name }}
PS: Your created hook should be:
created() {
axios.get('/api/game').then((response) => {
this.game.meta = response.data.meta;
this.game.empire = response.data.empire;
this.game.planets = response.data.planets;
});
},
router-view is a component from view-router which can help render named views. You can not pass empire and planets to it as those are props of your component.
You have to have following kind of code to pass empire and planets to your component:
<my-component :meta="meta" :empire="empire" :planets="planets"></my-component>
You can see more details around this here.