Using $refs in a computed property

2020-05-29 12:48发布

问题:

How do I access $refs inside computed? It's always undefined the first time the computed property is run.

回答1:

Going to answer my own question here, I couldn't find a satisfactory answer anywhere else. Sometimes you just need access to a dom element to make some calculations. Hopefully this is helpful to others.

I had to trick Vue to update the computed property once the component was mounted.

Vue.component('my-component', {
    data(){
        return {
            isMounted: false
        }
    },
    computed:{
        property(){
            if(!this.isMounted)
                return;
            // this.$refs is available
        }
    },
    mounted(){
        this.isMounted = true;
    }
})


回答2:

I think it is important to quote the Vue js guide:

$refs are only populated after the component has been rendered, and they are not reactive. It is only meant as an escape hatch for direct child manipulation - you should avoid accessing $refs from within templates or computed properties.

It is therefore not something you're supposed to do, although you can always hack your way around it.



回答3:

If you need the $refs after an v-if you could use the updated() hook.

<div v-if="myProp"></div>


updated() {
    if (!this.myProp) return;
    /// this.$refs is available
},


回答4:

For others users like me that need just pass some data to prop, I used data instead of computed

Vue.component('my-component', {
    data(){
        return {
            myProp: null
        }
    },    
    mounted(){
        this.myProp= 'hello'    
        //$refs is available              
        // this.myProp is reactive, bind will work to property
    }
})


回答5:

I just came with this same problem and realized that this is the type of situation that computed properties will not work.

According to the current documentation (https://vuejs.org/v2/guide/computed.html):

"[...]Instead of a computed property, we can define the same function as a method. For the end result, the two approaches are indeed exactly the same. However, the difference is that computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when some of its reactive dependencies have changed"

So, what (probably) happen in these situations is that finishing the mounted lifecycle of the component and setting the refs doesn't count as a reactive change on the dependencies of the computed property.

For example, in my case I have a button that need to be disabled when there is no selected row in my ref table. So, this code will not work:

<button :disabled="!anySelected">Test</button>

computed: {
    anySelected () {
      if (!this.$refs.table) return false

      return this.$refs.table.selected.length > 0
    }
}

What you can do is replace the computed property to a method, and that should work properly:

<button :disabled="!anySelected()">Test</button>

methods: {
    anySelected () {
      if (!this.$refs.table) return false

      return this.$refs.table.selected.length > 0
    }
}


回答6:

Use property binding if you want. :disabled prop is reactive in this case

<button :disabled="$refs.email ? $refs.email.$v.$invalid : true">Login</button>

But to check two fields i found no other way as dummy method:

<button
    :disabled="$refs.password ? checkIsValid($refs.email.$v.$invalid, $refs.password.$v.$invalid) : true">
            {{data.submitButton.value}}
</button>

methods: {
   checkIsValid(email, password) {
      return email || password;
   }
}


标签: vue.js