Alexander Kim
Alexander Kim

Reputation: 18411

How to access ref element that being conditionally rendered in v-if - Vue

I have an element that being conditionally rendered with v-if="isLogged", if a user is logged in:

<div
  v-if="isLogged"
  class="chatBlock"
  ref="chat"
></div>

I'm trying to get scroll height of the chat reference in a mounted () function - this.$refs.logged.scrollHeight, but it's not possible, because if a user is not logged in, then this div won't be rendered on a mounting stage, so even if a user logs in - it won't work, because mounted stage already passed.

Is it possible to track element appearance in a DOM using watch method?

UPDATE

Added watcher, as Steven suggested below in a mounted ():

this.$store.watch(
  (state) => {
    return this.$store.getters.isLogged
  },
  (newValue, oldValue) => {
    if (newValue) {
      this.chatHeight = this.$refs.chat.scrollHeight
    }
  }
)

Upvotes: 12

Views: 24555

Answers (4)

guizo
guizo

Reputation: 3125

Try adding a nexTick around the call to the ref. This way, the ref will exist when the code enters the block.

Demo:

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.8/vue.global.min.js"></script>

<div id="app">
  <div>
    <button @click="handleToggle" style="margin-right:0.5rem;">Toggle</button>
    <my-element v-if="visible" ref="myElement" />
  </div>
</div>

<script>
  const {
    createApp
  } = Vue

  createApp({
    components: {
      myElement: {
        template: "<span>{{ text }}</span>",
        data() {
          return {
            text: '',
          };
        },
      }
    },
    data() {
      return {
        visible: false,
      }
    },
    methods: {
      handleToggle() {
        this.visible = !this.visible;

        if (this.visible) {
          this.$nextTick(() => {
            this.$refs.myElement.text = "I'm visible";
          });
        }
      },
    }
  }).mount('#app')
</script>

Upvotes: 0

Nickolai
Nickolai

Reputation: 1748

Switching to v-show, as suggested by Steven, worked for me. It was actually the better option because v-show is cheap to toggle, unlike v-if, see this answer: https://stackoverflow.com/a/44296275/2544357

Upvotes: 1

Thomas Kainrad
Thomas Kainrad

Reputation: 2830

The accepted answer did not work for me. The watch does not guarantee that the element has already been rendered.

To make sure that the element is available, I had to use $nextTick

 if (newValue) {   
     this.$nextTick(function () {
         this.chatHeight = this.$refs.chat.scrollHeight
    })
}

This will cause the code to be executed after the next DOM update cycle.

Upvotes: 18

Steven Spungin
Steven Spungin

Reputation: 29169

Add a watch to isLogged. When it is active, get your chat ref. You will also have to check on your mounted, so put your logic in a common function.

So in your component:

val componentOptions = {
  methods: {
    checkDiv() {
      const chatDiv = this.$refs.chat
      if (chatDiv) {
        // your code here...
      }
    }
  },
  mounted() {
    this.checkDiv();
  },
  watch: {
    isLogged(value) {
      if (value) {
        this.checkDiv()
      }
    }
  }
}

—-

You can also use v-show instead of v-if. That way your element will be rendered but hidden.

Upvotes: 6

Related Questions