Search code examples
vuejs3vue-composition-api

How access to template ref in conditional rendering using Composition API?


I need access to DOM-element (set focus) which rendering through v-if condition:

<template>
    <input
        v-if="isInputShow"
        ref="input"
    >
    <button
        v-else
        @click="showInput"
    >Show input</button>
</template>
import { ref } from 'vue'

export default {
    setup() {
        const isInputShow = ref(false)
        const input = ref(null)

        const showInput = () => {
            if(!isInputShow.value) {
                isInputShow.value = true
                console.log(input.value) //null
            }
        }
        
        return{
            input,
            isInputShow,
            showInput
        }
    }
}

onMounted hook doesn't work (conditional rendering). Change v-if condition on v-show doesn't work (need to hide another element). Maybe I should use reactive() instead of ref()? But how?


Solution

  • You can watch the input ref, it could give a more concise solution:

    Vue SFC Playground

    <script setup>
    import { watch, ref } from 'vue'
    
    const isInputShow = ref(false)
    const input = ref(null)
    
    watch(input, input => {
        // if you show/hide the input with v-if, check it's not null
        input && console.log(input);
    });
    
    </script>
    
    <template>
        <input
            v-if="isInputShow"
            ref="input"
        >
        <button
            v-else
            @click="isInputShow = true"
        >Show input</button>
    </template>
    

    As mentioned by yoduh you could use nextTick() also. But it returns a promise so async/await would give a more clean solution imho:

    Vue SFC Playground

    <script setup>
    import { nextTick, ref } from 'vue'
    
    const isInputShow = ref(false)
    const input = ref(null)
    
    const showInput = async () => {
        if(!isInputShow.value) {
            isInputShow.value = true
            await nextTick();
            console.log(input.value) 
        }
    }
    </script>
    
    <template>
        <input
            v-if="isInputShow"
            ref="input"
        >
        <button
            v-else
            @click="showInput"
        >Show input</button>
    </template>