Vue.js setup() and mounted() - vue.js

I am fairly new to Vue.js and am currently working on a project. I currently have some constants declared in setup(), lets call one of the constant "c", and I want to get them from within mounted(). I realized that I can still console.log(this.c) within mounted() to get the constant printed out but when I try to console.log(this.c) within a function in mounted() I can no longer get it and I get an error Uncaught (in promise) TypeError: Cannot read properties of undefined.
Why is this so? And what work arounds are there?
Example:
setup() {
const c = ref(1)
},
mounted() {
console.log(this.c) // prints 1 on console
function doesSomething() {
console.log(this.c) // TypeError: Cannot read properties of undefined
...
}
}

The function doSomething in the mounted hook should be declared with an arrow function instead, since function declaration has its own this binding:
mounted() {
console.log(this.c)
const doesSomething = () => {
console.log(this.c) // Ok
}
}

mounted is an option in the options api which is different than the composition api using the setup hook, you could use onMounted hook in setup to replace mounted option:
import {onMounted} from 'vue';
setup(){
let c='some content'
onMounted(()=>{
console.log(c)// don't use this keyword
})
}

Related

wrapper.$options.watch.$route.call is not a function , I want to test the function when the route is changes in watch property vue

I am triggering a function when the route is changed in watch property
I used wrapper.vm.$options.watch.$route.call(wrapper.vm,'test1','test2)
It shows error wrapper.vm.$options.watch.$route.call is not a function
can someone please tell me what i'm missing
This is my code:
js file:
watch: {
$route() {
this.eventSearchQueryHandler()
console.log("hebdhdb")
},
jest file:
test('watch: $route', () => {
const wrapper = getWrapper()
const spy = jest.spyOn(wrapper.vm, 'eventSearchQueryHandler')
wrapper.vm.$options.watch.$route.call(wrapper.vm, '/tivo/deals')
expect(spy).toBeCalled()
})
I want to mock the watcher and check the function to be called.
Is there any other approach that i can trigger this watch and test the function to be called

Why is a variable declared in onBeforeMount() not known in the main section of <script>?

I have a Vue3 component in which I use a pre-mount hook (this is a trimmed-down version to scope it to the problem):
<script setup lang="ts">
const hello = () => {
let a = [... allNotes.value]
}
onBeforeMount(() => {
let allNotes = ref([])
}
</script>
With this, I get a ReferenceError: allNotes is not defined on the line that defines a.
Why is it so? Isn't allNotes known to hello() after the mount?
Why are you defining a reactive variable from inside a lifecycle hook? If you want allNotes to be available in the component or in the rest of your script, you only need to declare it in the top level of <script setup>. Remember that the Composition API setup() function replaces the beforeCreate and created lifecycle hooks, so anything defined within setup() will be available everywhere. You can read more about that in the Vue Docs
Specifically the problem here is that allNotes is scoped to the onBeforeMount() function only, and as such isn't known to the rest of your script. Once onBeforeMount() is called and finished, it will destroy allNotes and it will no longer exist.
You can just do
<script setup>
const allNotes = ref([])
const hello = () => {
allNotes.value.push("Hello")
}
</script>
To illustrate the point with the OptionsAPI, what you're doing is the same as something like this:
export default {
beforeMount() {
const allNotes = [];
},
methods: {
hello() {
this.allNotes.push("Hello!");
}
}
}
That won't work, since allNotes only exists inside of the beforeMount() hook. You'd have to declare allNotes in the data() or computed() properties in order for your hello() method to be able to use it.
Also, as a small sidenote, you should declare reactive elements with const and not let. It's a bit weird because you technically are changing its value, but the Vue internals make it so you're actually changing a copy of it that exists inside of Vue. As such, you're not actually modifying the original value, so let is inappropriate.

vue3: control property with a timed function

First of all, I am a new vuejs developer and my purpose is to get acquainted with Vue, so, not going to use any external plugins or components.
I am writing a simple alert component, which looks like this:
<Alert :show="showAlert" />
I want the show property to return back to false after 2 seconds. How can I do this from inside the component (i.e., not in the page where this component is used). I tried this:
import { computed } from 'vue';
export default {
props: ['show'],
setup(props) {
const shown = computed(() => {
if (props.show) {
setTimeout(() => {
console.log("hiding the alert...")
props.show = false
}, 2000);
}
return props.show.value
})
return { shown }
}
};
the compiler said:
14:15 error Unexpected timed function in computed function vue/no-async-in-computed-properties
16:19 error Unexpected mutation of "show" prop vue/no-mutating-props
My rational is that the delay of alert should be controlled by the alert component (which could be changed by a prop), but not forcing the caller to write some thing like:
function Alert(delay) {
showAlert = true
setTimeout(() => showAlert = false, delay)
}
There are 2 errors.
First vue/no-mutating-props, props are read only so you are not supposed to change it from within the component. It is still possible to change props from outside the component and pass down to it.
For this you should copy the value of props to your data()
data() {
return {
showAlert
}
}
You should be able to update showAlert with no problem.
The second error vue/no-async-in-computed-properties, you cannot write async function inside computed(), so the alternative is to use watch instead.

Vue js, cannot read property $el

I have the problem with correctly understood the flow elements, method calling in vue js. It is the standard idea - fetching some data from rest api, and render it on the browser.
The getting method I wrote into mounted(). Also I added there calling renderHomePageMethod(). This method was written in methods:
mounted() {
axios.get("http://localhost:3000/api/test").then(response => {
this.testData= response.data
this.renderHomePageMethod();
});
}
In renderHomePageMethod() I used this.refs$ and $el. And probably there is the problem, everything is working fine, but in the browser I got warning about:
Uncaught (in promise) TypeError: Cannot read property '$el' of undefined
Probably I should calling
this.renderHomePageMethod()
in another place. But where?
It seems like your referenced component is not rendered before the main component renders, so it gives a reference error.
A hackish way would be something like this:
mounted() {
axios.get("http://localhost:3000/api/test").then(response => {
this.testData= response.data
setTimeout(() => {
this.renderHomePageMethod();
}, 1000); // or any other minimum delay before the subcomponent is rendered
});
}
or the better and harder way, create an event-bus.js file which contains:
import Vue from 'vue';
export const EventBus = new Vue();
in your main and sub components:
import { EventBus } from "./event-bus.js";
in your sub component, this will send the notification to the main component when it's ready to roll:
mounted(){
EventBus.$emit("subcomponent:is-mounted");
}
in your main component:
data(){
return {
testData: null
}
},
mounted(){
axios.get("http://localhost:3000/api/test").then(response => {
this.testData= response.data
});
EventBus.$on("subcomponent:is-mounted", () =>{
this.renderHomePageMethod();
});
},
beforeDestroy(){
EventBus.$off("subcomponent:is-mounted");
// don't forget to remove the listeners because of duplicate listeners may occur
// if your component refreshes (remounts)
}

How to pass variable from Vuex store to a function as value?

So I am working with what would appear to be a simple issue, but it is eluding me this evening. I have a value that is set in a Vuex store. In my component file, I declare a constant where the value is retrieved from the store. Everything up to this point works perfectly.
Then, upon submitting a form in the component a script function is run. Within that function, I need to pass the value from the Vuex store along with a couple of other arguments to another function. The function gets call, the arguments are passed, and it all works as expected.
However ... I am getting console errors stating ...
Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers.
What is the correct what to retrieve a value from the Vuex store and then pass that value to a function?
Some more detail here ... Page 1 stores an object representing a CognitoUser in the store using a mutation function which works as expected, then transitions to Page 2. Page 2 retrieves the object from the store (tried both the data and computed methods mentioned below as well as using the getter directly in the code - all fail the same). Within a method on Page 2, the object from the store is accessible. However, that method attempts to call the Amplify completeNewPassword method, passing the CongnitoUser object as an argument. This is the point that the error appears stating that the mutation handler should be used even though there is no change to the object on my end.
....
computed: {
user: {
get(){
return this.$store.getters[ 'security/localUser' ]
},
set( value ){
this.$store.commit( 'security/setLocalUser', value )
}
}
},
....
methods: {
async submitForm(){
this.$Amplify.Auth.completeNewPassword( this.user, this.model.password, this.requiredAttributes )
.then( data => {
....
This is almost certainly a duplicate question. You can refer to my answer here.
Basically you should pass the Vuex value to a local data item and use that in your component function. Something like this.
<script>
export default {
data: () => ({
localDataItem: this.$store.getters.vuexItem,
})
methods: {
doSomething() {
use this.localDataItem.here
}
}
}
</script>
The canonical way of handling this by using computed properties. You define a computed property with getter and setter and proxy access to vuex thru it.
computed: {
localProperty: {
get: function () {
return this.$store.getters.data
},
set: function (val) {
this.$store.commit(“mutationName”, val )
}
}
}
Now you can use localProperty just as we use any other property defined on data. And all the changes get propagated thru the store.
Try if this work
<template>
<div>
<input :value="user" #change="onChangeUser($event.target.value)"></input>
</div>
</template>
<script>
computed: {
user() {
return this.$store.getters[ 'security/localUser' ]
}
},
methods: {
onChangeUser(user) {
this.$store.commit( 'security/setLocalUser', user );
},
async submitForm(){
this.$Amplify.Auth.completeNewPassword( this.user, this.model.password, this.requiredAttributes )
.then( data => {
...
}
</script>