Vue not seeing function passed from separate component - vue.js

Inside component A I have a watch object like this:
watch: {
delete_todo_object: {
handler(object) {
if (object.error) {
this.showSnackBar({
text: `Could\'nt delete task. Reason: ${object.error}`,
color: "error",
close_button_text: "Close",
close_button_function: () => hideSnackBar()
});
}
},
deep: true
},
and a function like this:
methods: {
hideSnackBar() {
this.$store.commit("notifications/hideSnackBar");
},
close_button_function is correctly finding the hideSnackBar function I have inside component A and passing it along to my vuex module. Component B has a computed property that returns the same object stored in the store.
computed: {
snackbar_object () {
return this.$store.state.notifications.snackbar;
}
},
However, when component B tries to use the function, it says "hideSnackBar is not defined".
<v-btn
color="primary"
flat
#click="snackbar_object.close_button_function"
>
I checked and made sure the function is being sent along to my vuex store and assigned to the right object property there.
Is what I'm trying to do not possible?

You call hideSnackBar as if it exists in showSnackBar context.
close_button_function: () => hideSnackBar()
Please, try
close_button_function: () => this.hideSnackBar()

Related

How to compute a property based on an object with fallback

I have a component that receives an object as prop, like this:
props: ['propObject']
Then, there's a default object defined (I use VueX, so it's actually defined as a $store getter, but to make it simpler, let's say it's defined in the data method) in the data:
data() {
return {
dataObject: {defaultValueA: 1, defaultValueB: 2}
}
}
And I'd like to have a computed property that would behavior like this:
computed: {
computedObject() {
return Object.values(this.propObject).length > 0 ? this.propObject : this.dataObject;
}
}
However, I know this is not possible because Vue watchers don't watch for changes in the key/value pairs of an object.
I have tried to go with a watched property, like this:
props: ['propObject'],
data() {
return {
object: {},
defaultObject: {}
}
},
watch: {
propObject: {
handler: function() {
this.setComputedObject();
},
deep: true
}
},
methods: {
setComputedObject() {
this.object = Object.values(this.propObject).length > 0 ? this.propObject : this.defaultObject;
}
},
mounted() {
this.setComputedObject();
}
However, the watcher handler is not being called at all when the propObject changes, but if I call it directly via console, it works. Is there any way that I can make the computedObject become reactive?
you need to use Vue.set/vm.$set where you change the props (in source component)
for example
changeProp(){
this.$set(propObject,'newprop','newval');
}
and then just you regualr compouted in the target component (the component which receive the prop)
source : https://v2.vuejs.org/v2/guide/list.html#Object-Change-Detection-Caveats

Computed property that depends on another computed property

Code below yields error "Cannot read property 'form' of undefined":
computed: {
boundary_limits () {
return this.$refs.hemoBoundaryLimits.form;
},
high_risk_alerts () {
return this.$refs.highRiskAlerts.form;
},
alerts () {
return {
boundary_limits: this.boundary_limits,
high_risk_alerts: this.high_risk_alerts
}
}
}
Yet if I removed alerts(), I get no error and I can even console log boundary_limits or high_risk_alerts
successfully, which means $refs.hemoBoundaryLimits and this.$refs.highRiskAlerts are defined.
So Vue.js has a problem with how I define alerts, but I see no problem in it.
Any clue what's going on?
The error comes because you are trying to access $refs in computed property.
Computed properties are evaluated before the template is mounted and thus the hemoBoundaryLimits is undefined.
You should access $refs in the mounted hook.
As solution, you can trick it by knowing when the component is mounted using #hook event:
<hemoBoundaryLimits #hook:mounted="isHemoBoundaryLimitsMounted = true" />
And in the script
data: () => ({
isHemoBoundaryLimitsMounted: false
}),
computed: {
boundary_limits () {
if (!this.isHemoBoundaryLimitsMounted) return
return this.$refs.hemoBoundaryLimits.form;
}
}

Something will trigger a warning in the console? Why is it not recommended and how would you fix it?

it's about good practice and knowledge on Javascript and VueJs.
I already commented the Type Api to see if other warnings persist
<script>
export default {
name: "printer",
props: {
model: {
type: String,
required: true
},
ip: {
type: String,
required: true
},
jobs: {
type: Object,
validator: (value) => ["inProgress", "done", "error"].every((status) => typeof value[status] === "number")
},
progress: {
type: Number,
required: true
},
api: {
type: Api,
required: true
}
},
data: () => ({
timer: null
}),
methods: {
async refreshProgress() {
this.jobs = await this.api.getJobsNumbers({ model: this.model });
}
},
created() {
this.timer = setInterval(() => this.refreshProgress(), 30000);
},
destroyed() {
clearInterval(this.timer);
}
};
</script>
So I'm looking for advices as I'm a beginner in VueJS and need to describe and purpose changements
Question is missing exact warning text so I'm just guessing the warning you talk about is probably something like this:
[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: "jobs"
In other words you are mutating parent's state in child component which is not a good idea and that's why Vue warns you.
There are several ways how to fix it:
make a copy of props and store it in components data. Update internal state only. Changes will not be visible to parent
Instead of mutating jobs prop, fire a custom event passing new state (returned from API call) as an argument. Parent needs to handle the event and update its own state with new value (update will flow through prop back to child component)
If you are absolutely sure your printer component will be the only place where jobs object will be mutated, you can mutate object's properties instead of replacing whole object without any warnings. This is possible but not recommended...
For more see Vue documentation or some very related SO questions

When passing data from parent component to child component via props, the data appears to be undefined in the mounted hook of the child component

In my parent component:
<UsersList :current-room="current_room" />
In the child component:
export default {
props: {
currentRoom: Object
},
data () {
return {
users: []
}
},
mounted () {
this.$nextTick( async () => {
console.log(this.currentRoom) // this, weirdly, has the data I expect, and id is set to 1
let url = `${process.env.VUE_APP_API_URL}/chat_room/${this.currentRoom.id}/users`
console.log(url) // the result: /api/chat_room/undefined/users
let response = await this.axios.get(url)
this.users = response.data
})
},
}
When I look at the page using vue-devtools, I can see the data appears:
I've run into this issue in the past – as have many others. For whatever reason, you can't rely on props being available in the component's mounted handler. I think it has to do with the point at which mounted() is called within Vue's lifecycle.
I solved my problem by watching the prop and moving my logic from mounted to the watch handler. In your case, you could watch the currentRoom property, and make your api call in the handler:
export default {
props: {
currentRoom: Object
},
data() {
return {
users: []
}
},
watch: {
currentRoom(room) {
this.$nextTick(async() => {
let url = `${process.env.VUE_APP_API_URL}/chat_room/${room.id}/users`
let response = await this.axios.get(url)
this.users = response.data
})
}
},
}
I don't think you really need to use $nextTick() here, but I left it as you had it. You could try taking that out to simplify the code.
By the way, the reason console.log(this.currentRoom); shows you the room ID is because when you pass an object to console.log(), it binds to that object until it is read. So even though the room ID is not available when console.log() is called, it becomes available before you see the result in the console.

Calling a function with a paramater in click causes error

I have a computed Vue function that has a parameter. Every time I try to bind it to the click, I receive and error Error in event handler for "click": "TypeError: searchHashtag is not a function"
Here's the HTML:
<el-button #click="searchHashtag('#acc')">Test</el-button>
And here's the logic:
data () {
messages: []
},
mounted () {
api.fetchMessages( this.projectId, ( data ) => {
this.messages = data.messages;
},
computed: {
searchHashtag (searchBy) {
if (_.contains(this.messages, searchBy))
this.$message('This is a message.');
}
}
You want a method, not a computed property.
methods: {
searchHashtag (searchBy) {
if (_.contains(this.messages, searchBy))
this.$message('This is a message.');
}
}
Computed properties cannot be called like a function. They act like properties of the Vue and do not take arguments. When you need a function that accepts arguments, always use a method.