Vue Computed Undefined

Posted on  by admin

Modified2 years, 9 months ago. I just wanna use Vue computed, but I get undefined(styleObj.width), and it seems that the computed function was not called (didn't log 'computed').

And when I changed data while the computed function was still not be called and the data (styleObj.width) was still undefined.

The code is simple and I hope you know what I'm talking about. I have 2 questions:. Why the calcWidth function never be called?

I think it will be called twice, at the beginning and in 2 sec, but it never be called. Why the styleObj.width has always been undefined? 2626 bronze badges. 48211 gold badge66 silver badges1414 bronze badges.

66 silver badges1414 bronze badges. There are several problems.

The way it's written currently, the this in width:this.calcWidth will not be referring to the correct object. It needs to be within a function to get the scoping correct.

That is easily fixed by changing data to a function. The next problem is that computed properties are not available within the data function.

  • The order is roughly:. Things lower down that list can use things higher up the list, not the other way around.
  • Instead you can make the whole style object a computed property:. or if you prefer to retain calcWidth:.

33 gold badges3333 silver badges5353 bronze badges.

Table of Contents

5353 bronze badges. You can't use computed property in data, because data evaluates before the computed properties did. You can use a watcher to achieve the intended result:.

If num changes the watcher get triggered.See the documentation on that. Edit:You can use the argument immediate to trigger directly:.

Conclusion

I have installed in my Vue.js app the npm package https://github.com/hyounoo/v-treeview.

I have a complex tree structure with depths that can be fairly large ~ 10.

I decided to get the depth = 1 of the tree when the page is mounted to avoid loading the entire tree structure at once.

This works very well, and my top parent nodes are being correctly displayed, along with their respective icons. What I am trying to do next is quite simple, upon selection of one of the nodes, I call a Koa API to retrieve the children of the selected node.

This is an async method. My issue is that although I managed to update the treeData object with the new child nodes, the page does not display the new tree structure.

  • I compared the Vue components in between the demo site for the npm package and my app.
  • I found that the isFolder computer property remains undefined for the top parents’ nodes, while at the same time another computed function icon() has data.
  • According to the NPM package source file, the isFolder computed function is defined by.

Can someone please explain what could cause the computed function isFolder to evaluate to undefined even after treeData object is updated?

References

I’m sure I’m not understanding some concepts with how Vue.JS works, I hope someone can give me pointers to successfully update the sub-nodes on demand. Thank you in advance for your help. Note: This article assumes that you already understand the basic of Vue Component.

If not, please do read the Vue component documentation first.

The ref attribute is our last shot to manipulate DOM if any other way can't be used.

Some of explanations of ref attribute are also listed under Handling Edge Cases section on Vue documentation.

And it also stated that we have to avoid manipulating DOM elements directly as best as we can. So, please check thoroughly if you want to use ref feature in Vue.

It doesn't mean we can't use that feature. It is just as a reminder that we have to make sure it will be worked as our expected. We have a Vue component as follow:.

It has an input which ref attribute is set and a paragraph containing data binding. Let say we want to make the input focused programmatically.

We can do that by calling $refs.input.focus(). The problem is, sometimes we can't get the access to the corresponding input refs we want.

Because ref itself is created as a result of the render function. More about this explanation, you can learn more on Ref API - Vue.js.

Preface

Before jump into the next section, let's refresh our understanding about the ref and $refs a little bit. ref is an HTML attribute or component props. Its value will be the name of the reference.

On the other hands, $refs is an instance property.

  • We can access the reference name we already declared using ref from this property.
  • Using the example above, we give input as ref attribute value. So we can access it using this way, $refs.input.
  • We have to know where and when we access that property.

Here some examples of accessing $refs from different places:.

Accessing $refs.input directly on created is undefined. As I described earlier above, the ref is created as a result of the render function.

  • And createdis called before render function.
  • But, a weird thing happens if we only print $refs.
  • The console output will show the input property. Why does that happen?

Dion DiFelice gave a concise explanation on StackOverflow thread, Vue.js refs are undefined, even though this.$refs show they're there:.

is that self.refs is passed by reference. So, by the time you viewed the console, the rest of the data finished loading in a matter of milliseconds.

And self.$refs.mapRef is not passed by reference, so it remains undefined, as it was during runtime.

That explanation also applies if we access $refs on another Vue.js lifecycle except mounted and beforeDestroy. Accessing $refs.input.value directly in computed property will yield the following error on the console:.

It is because the input property itself is undefined, so we can't get access to the value property.

Besides that, this error also raises:. That is because of the previous error happens during component rendering. So the computed value will be undefined as well. Watching change on input value won't do anything, because $refs are not reactive.

On the subsection of Handling Edge Cases, Accessing Child Component Instances & Child Elements, it states that:.

Careful Way

$refs are only populated after component has been rendered, and they are not reactive. That will be the same if we only watch the input because the $refs property itself is not reactive. Accessing $refs in methods is a little bit tricky.

Because it also depends on when and where we call that method. So make sure to not call the method where $refs is undefined.

Probably the safest place to access $refs is on mounted. Accessing $refs.input and $refs will yield value. But if we want to add ref attribute to a component, we have to confirm that our component isn't loaded as a asynchronous component.

Even though we access $refs on mounted, it will return undefined. We usually use $nextTick to overcome getting undefined value problem. But for this case, even though we use multiple levels of $nextTick. It still won't solve the problem. Learn more about $nextTick on Async Update Queue - Vue.js.

Using setTimeout will delay the execution of the callback function for a certain amount of time. It can work as expected if we know how much delay we can have.

And the delay itself depends on the user connectivity as well. The slower internet, the longer the delay will be.

Safe Way

Let say we can't rely on all the previous method we have. So what can we do if we need to access $refs safely? Before we continue, let me tell the story of how I found the solution.

A couple days ago, I discovered that the interactive demo on "Create a Simplified Version of Vue.js Reactivity System", both on Part 1 and Part 2 weren't worked on a production build.

I was suspicious, it was caused by tag that can't be executed inside on production.

I seek another method to inject and run JavaScript after the browser finish loads the chunk.

I decided to use $refs. But the problem was the component is loaded asynchronously. So I have to use, either $nextTick or setTimeout to delay the injection script. Unfortunately, none of them works as I expected.

Methods

Then I recalled, I've used an NPM Package called wait-for-expect.

The package is useful for waiting Jest expectation if we run asynchronous code until the result is expected. The idea behind the package is, it runs the callback function containing the expectation code continuously for a certain period.

It will stop when the callback function is resolved or not throws any error.

Perhaps I use that mechanism to get $refs value without getting undefined. Starting from the safest place to get $refs value, we create a variable called interval and assigned it to setInterval calls. It will run the callback function every 50ms.

We can adjust that 50ms time according to our needs, but here I just follow what wait-for-expect did. Inside the callback function, we do checking if $refs.nav exists, we do something with it, print it to the console output for instance.

Then, we call clearInterval function and pass interval variable to clear up the memory and stop the callback function invocation.

By doing that, we don't have to know how long the delay until $refs.nav is available. As long as the interval is running, it will execute the callback and do if checking for that. If you want to know the implementation in the interactive demo, please take a look at AppDemo10En.vue. $refs is one of the tricky features to use in Vue.js.

Watch

To use it we have to do it at the right time and the right place. Even if we already did, sometimes the value is still undefined.

The safest place to use $refs is on mounted. Other than that, it is prone to be undefined.

So, we have to be careful before using it. To solve that problem, we can make use of setInterval.

Computed

We put that in the safest place to access $refs, which is in mounted. Inside the callback function we do checking if $refs is already available, then we can do anything with it and the most important thing is stop the timer using clearInterval.

Disclaimer: This method is tested according to my needs.

I can't ensure that it also works for other use cases. Please do check thoroughly when implementing.

Hit me up on Twitter @jefrydco, if you face any obstacle.