Vuejs watching an object then execute a method fails - javascript

I want to watch a prop which is an object so i have
<script>
export default {
watch:{
filter: {
handler:(newval)=> {
console.log("i have new data",newval) //this works
this.fetchData(); //throws an error
},
deep: true
}
},
props:{
filter:{
type:Object,
required:true
}
},
data: () => ({
pagination: {},
items: []
}),
methods:{
fetchData(){
console.log("am fetching the data");
}
}
}
The above watcher works as the console.log displays the new value but i cannot execute a method as on the watch am getting an error Error in callback for watcher "filter": "TypeError: _this.fetchData is not a function". How can i execute a method on a deep watcher.

Move Arrow function to simple function for handler method. Change handler:(newval)=> { to handler: function (newval) {:
Vue.js docs:
Don’t use arrow functions on an options property or callback, such as created: () => console.log(this.a) or vm.$watch('a', newValue => this.myMethod()).
handler: function (newval) {
console.log("i have new data",newval) //this works
this.fetchData(); // it should work
},

Related

How to pass object handler into vue component

I want to cache state inside a handler passed by props.
Problem is, the template renders and text content changes well, but the console always throw error:
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'apply' of undefined"
Codes below:
<template>
<picker-box :options="item.options" #change="handlePickerChange($event, item)">
<text>{{ item.options[getPickerValue(item)].text }}</text>
</picker-box>
</template>
<script>
export default {
props: {
item: {
type: Object,
default() {
return {
options: [{ text: 'op1' }, { text: 'op2' }],
handler: {
current: 0,
get() {
return this.current
},
set(value) {
this.current = value
},
},
/* I also tried this: */
// new (function () {
// this.current = 0
// this.get = () => {
// return this.current
// }
// this.set = (value) => {
// this.current = value
// }
// })(),
}
},
},
},
methods: {
handlePickerChange(value, item) {
item.handler.set(value)
},
getPickerValue(item) {
return item.handler.get()
},
},
}
</script>
I know it's easy using data() or model prop, but I hope to cahce as this handler.current, in other words, I just want to know why this handler object isn't correct (syntax layer), how can I fix it.
What exactly state are you trying pass?
If you need to keep track of a static property, you could use client's localstorage, or a watcher for this.
If you need to keep of more dynamic data, you could pass it to a computed property that keeps track and sets them everytime they change.

The this is undefined if used inside a debounce function

For some reason the this is undefined if I'm using a debounce function. I have tried to bind it but still it is undefined. I can't understand what is happening here..
For example here this is working and returns
VueComponent {_uid: 6, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
<template>
<at-ta #at="fetchMembers">
<textarea></textarea>
</at-ta>
</template>
fetchMembers(at)
{
console.log(this) // <-- VueComponent { ...
},
But when I move it to a debounce function it isn't working anymore but is
this is undefined
fetchMembers: debounce(at => {
axios.get(`/api/users?name=${at}`).then(({data}) => {
this.members = data // <-- this is undefined
})
}, 500)
Don't bind the debounced function, but do bind the axios promise callback.
methods: {
fetchMembers: debounce(function (at) {
axios.get(`/api/users?name=${at}`).then(({data}) => {
this.members = data
})
}, 500)
}
It didn't work for you because you used a fat arrow function for the debounced function where this is undefined at that scope.
Keep in mind that you're only creating one debounced function that will be shared across all instances of your component. If this is an issue, you can instead wrap fetchMembers with a debounced function in the created hook:
created() {
this.fetchMembers = _.debounce(this.fetchMembers, 500)
},
methods: {
fetchMembers() {
axios.get(`/api/users?name=${at}`).then(({data}) => {
this.members = data
})
}
},

call function inside data() property

I'm trying to fetching some data for my search tree and i'm not able to get the data directly from axios or to call a function because it can't find this.
export default {
name: 'SideNavMenu',
data () {
return {
searchValue: '',
treeData: this.getData(),
treeOptions: {
fetchData(node) {
this.onNodeSelected(node)
}
},
}
},
In the data() I have treeOptions where I want to call a function called onNodeSelected. The error message is:
"TypeError: this.onNodeSelected is not a function"
can anybody help?
When using this, you try to call on a member for the current object.
In JavaScript, using the {} is actually creating a new object of its own and therefore, either the object needs to implement onNodeSelected or you need to call a different function that will allow you to call it on an object that implements the function.
export default {
name: 'SideNavMenu',
data () {
return {
searchValue: '',
treeData: this.getData(), // <--- This
treeOptions: {
fetchData(node) {
this.onNodeSelected(node) // <--- and this
}
},
}
},
//are calling functions in this object :
{
searchValue: '',
treeData: this.getData(),
treeOptions: {
fetchData(node) {
this.onNodeSelected(node)
}
},
//instead of the object you probably are thinking
I would avoid creating object blocks within object blocks like those as the code quickly becomes unreadable and rather create functions within a single object when needed.
I am guessing you would have the same error message if you tried to get a value from treeData as well
You are not calling the function, or returning anything from it. Perhaps you're trying to do this?
export default {
name: 'SideNavMenu',
data () {
return {
searchValue: '',
treeData: this.getData(),
treeOptions: fetchData(node) {
return this.onNodeSelected(node)
},
}
},
Regardless, it is not considered good practice to put functions inside data properties.
Try declaring your variables with empty values first, then setting them when you get the data inside beforeCreate, created, or mounted hooks, like so:
export default {
name: 'SideNavMenu',
data () {
return {
searchValue: '',
treeData: [],
treeOptions: {},
}
},
methods: {
getData(){
// get data here
},
fetchData(node){
this.onNodeSelected(node).then(options => this.treeOptions = options)
}
},
mounted(){
this.getData().then(data => this.treeData = data)
}
},
Or if you're using async await:
export default {
name: 'SideNavMenu',
data () {
return {
searchValue: '',
treeData: [],
treeOptions: {},
}
},
methods: {
getData(){
// get data here
},
async fetchData(node){
this.treeOptions = await this.onNodeSelected(node)
}
},
async mounted(){
this.treeData = await this.getData()
}
},

How to unit test the state of props using mocha chai and enzyme?

I'm trying to unit test onClick the state of the props in my component clear.
I tried doing it this way:
props = {
attributeTableData: data,
clearMessage: onClickMethod,
reset: () => { },
resetAttributeTable: () => { },
statusMessage: {
messageType: 'message-success',
userMessage: 'Template has been saved successfully. Please wait …see your results display with the latest'
},
submitTemplateCreationStatus: () => { },
templateAttributeFormData: () => { },
templateFormSubmission: true,
templateAttributeFormSubmission: true,
templateFormData: () => { },
userRoles: new Set(['admin'])
};
let emptyStatusMessage = {};
actualComponent = shallow(<CreateTemplateResults { ...props } />);
actualComponent.instance().resetForms();
expect(onClickMethod.called).to.be.true;
expect(actualComponent.state('statusMessage')).to.eql(emptyStatusMessage)
But I get:
" TypeError: ShallowWrapper::state("statusMessage") requires that
state not be null or undefined"
You are creating a shallow render of <CreateTemplateResults />, but never passing actualComponent.setState(nextState). Therefore, when you are trying to access the state on your last line, it is throwing an error because state is null/undefined.
shallow().setState

How to use data from one hook to other hook in Vue.js?

In my vue.js application I send request by axios package in created() hook. I add response to array called coordinates. I want to use that array outside of created() hook. For example in mounted() hook or in functions which we can set in methods.
Right now when I tried to use self.coordinates outside created() hook it return undefined. When I use this.coordinates it return just [__ob__: Observer].
Whats wrong I did?
export default {
name: "Map",
data() {
return {
coordinates: [],
}
},
created() {
let self = this;
axios.get('URL').then(function (response) {
let coordinates = [];
for (let i = 0; i < response.data.length; i++) {
coordinates.push([response.data[i]["LATITUDE"], response.data[i]["LONGITUDE"]]);
}
self.coordinates = coordinates;
});
},
mounted() {
console.log(self.coordinates); // undefined
consol.log(this.coordinates); // [__ob__: Observer]
},
}
I would prefer "mounted" and move the logic into methods for reusability. The method can be kicked from anywhere afterwards. In the example below, I prefered kicking the method direcly. Watchers is another option.
Here is the fiddle https://jsfiddle.net/dj79ux5t/2/
new Vue({
el: '#app',
data() {
return {
coordinates: []
}
},
mounted() {
let self = this;
axios.get('https://api.weather.gov/').then(function (response) {
self.coordinates = response.data;
self.greet();
});
},
methods: {
greet: function () {
console.warn(this.coordinates.status);
}
}
})
I think instead of mounted , you should use watch . You call some link so it will take time to load that data , watch method will trigger when your data is updated ...
watch: {
coordinates: {
handler: function (updateVal, oldVal) {
console.log(updateVal)
},
deep: true
}
},

Categories

Resources