在计算属性中使用$ refs

时间:2017-04-20 23:56:36

标签: vue.js

如何在计算机内部访问$refs?第一次运行计算属性时始终未定义。

10 个答案:

答案 0 :(得分:73)

在这里回答我自己的问题,我无法在其他任何地方找到满意的答案。有时你只需要访问一个dom元素来进行一些计算。希望这对其他人有帮助。

安装组件后,我不得不欺骗Vue更新计算属性。

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

答案 1 :(得分:6)

如果您在$refs之后需要v-if,则可以使用updated()挂钩。

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


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

答案 2 :(得分:6)

我认为引用Vue js guide很重要:

  

$ refs仅在呈现组件之后填充,并且它们是无反应的。它只是作为直接子操作的转义窗口-您应避免从模板或计算的属性中访问$ refs。

这不是您应该做的,尽管您总是可以绕开它。

答案 3 :(得分:2)

对于像我这样需要将一些数据传递给道具的其他用户,我使用data代替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
    }
})

答案 4 :(得分:1)

根据需要使用属性绑定。 :禁用的道具在这种情况下是反应性的

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

但是要检查两个字段,我发现没有其他方法可以用作虚拟方法:

<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;
   }
}

答案 5 :(得分:0)

我刚遇到同样的问题,并且意识到这是计算属性无法使用的情况。

根据当前文档(https://vuejs.org/v2/guide/computed.html):

“ [...] 我们可以定义与方法相同的功能,而不是计算属性。对于最终结果,这两种方法确实完全相同。但是,不同之处在于计算属性会根据其反应性依赖项进行缓存。只有在其某些反应性相关性发生变化时,计算属性才会重新评估

因此,在这些情况下(可能)发生的事情是,完成组件的已安装生命周期并设置引用不会算作对计算属性依赖性的反应性更改。

例如,在我的情况下,当我的参考表中没有选定的行时,我需要禁用一个按钮。 因此,此代码将不起作用:

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

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

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

您可以做的是将计算出的属性替换为方法,并且应该可以正常工作:

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

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

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

答案 6 :(得分:0)

我遇到了类似的情况,我用以下方法解决了:

  data: () => {
   return { 
    foo: null,
   }, // data

然后你观察变量:

    watch: {
     foo: function() {
       if(this.$refs)
        this.myVideo = this.$refs.webcam.$el;
       return null;
      },
    } // watch

注意评估 if 是否存在的 this.$refs,当它发生变化时,您会得到数据。

答案 7 :(得分:0)

你应该改变你的代码设计,但是当你需要的时候,检查这个;

https://github.com/posva/vue-reactive-refs

有关此问题的更多信息: https://github.com/vuejs/vue/issues/3842

答案 8 :(得分:0)

我所做的是将引用存储到数据属性中。然后,我在挂载事件中填充此数据属性。

    data() {
        return {
            childComps: [] // reference to child comps
        }
    },
    methods: {
        // method to populate the data array
        getChildComponent() {
            var listComps = [];
            if (this.$refs && this.$refs.childComps) {
                this.$refs.childComps.forEach(comp => {
                    listComps.push(comp);
                });
            }
            return this.childComps = listComps;
        }
    },
    mounted() {
        // Populates only when it is mounted
        this.getChildComponent();
    },
    computed: {
        propBasedOnComps() {
            var total = 0;
            // reference not to $refs but to data childComps array
            this.childComps.forEach(comp => {
                total += comp.compPropOrMethod;
            });
            return total;
        }
    }

答案 9 :(得分:0)

另一种方法是完全避免 $refs 并且只订阅来自子组件的事件。

它需要在子组件中有一个显式的 setter,但它是反应式的,不依赖于挂载时间。

父组件:

<script>
{
  data() {
    return {
      childFoo: null,
    }
  }
}
</script>

<template>
  <div>
    <Child @foo="childFoo = $event" />

    <!-- reacts to the child foo property -->
    {{ childFoo }}

  </div>
</template>

子组件:

{
  data() {
    const data = {
      foo: null,
    }
    this.$emit('foo', data)
    return data
  },
  emits: ['foo'],
  methods: {
    setFoo(foo) {
      this.foo = foo
      this.$emit('foo', foo)
    }
  }
}

<!-- template that calls setFoo e.g. on click -->