RamoFX
RamoFX

Reputation: 536

VueJS mounted hook in v-on directive

How can I call a method in VueJS when some html elements are mounted?

For example:

...
data: [
  {
    items: [
      {text:'item1'},
      {text:'item2'}
    ]
  }
]
methods: function() {
  myMethod: function(a) {
    console.warn(a)
  }
}
template: '<div
             v-for="item in items"
             :key="item.text"
             @hook:mounted="myMethod(item.text + \" mounted\")"
           >{{ item.text }}</div>'
...

I need to get HTML like this:

<div>item1</div>
<div>item2</div>

And I need to get 2 warn messages in console:

item1 mounted
item2 mounted

Or I can do it with another way?

Upvotes: 2

Views: 14045

Answers (3)

skirtle
skirtle

Reputation: 29092

As others have noted, the hook:mounted event is only available for components and won't fire for HTML elements.

You can do something similar for elements using a custom directive. While an element can't be 'mounted' we can nevertheless have a directive that is called when the element is available.

new Vue({
  el: '#app',

  directives: {
    myMountedDirective (el, { value }) {
      console.warn(value + ' mounted')
    }
  },

  data () {
    return {
      items: [
        {text: 'item1'},
        {text: 'item2'}
      ]
    }
  }
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>

<div id="app">
  <div
    v-for="item in items"
    :key="item.text"
    v-my-mounted-directive="item.text"
  >
    {{ item.text }}
  </div>
</div>

However, all of this is very DOM-driven. Custom directives exist so that tweaks can be made to the DOM nodes after they exist. Generally that isn't the correct way to solve the underlying problem.

Usually it is better to make decisions based on the data rather than the DOM. So rather than watching for changes to the DOM we can watch for changes in items.

If you really do need access to the DOM nodes (e.g. to measure sizes) then you might be better off using refs. You can add a ref attribute to each <div> and they will then be accessible as an array via $refs. The mounted and updated hooks of the surrounding component are generally the appropriate places to react to changes in the $refs as they all called immediately after rendering.

Upvotes: 2

gabriel.hayes
gabriel.hayes

Reputation: 2313

The only way to simulate a component mounting on divs is to actually create a component that is a div:

V-Div.vue

<template>
 <div><slot></slot></div>
</template>

You need a <slot> so that you can pass content into the <v-div>

Include V-Div in your component (the component you created, not V-Div.vue)

import VDiv from './components/V-Div.vue';
// ...
{
  data() { return { /* data */} },
  computed: {},
  // other options....
  components: {VDiv}
}

Use the VDiv in your component template:

<v-div @hook:mounted="myMethod(item.text + \" mounted\")">{{item.text}}</v-div>

EDIT: I would not recommend doing this, turning a plain HTML Element into a Vue component just to subscribe to life-cycles is a waste of resources.

Upvotes: 2

gabriel.hayes
gabriel.hayes

Reputation: 2313

Well, the @hook:mounted syntax adds a life-cycle hook to a Vue Component.

<div></div> is a plain HTML element.. you need to add a mounted() method to your component options. The reason @click works and @hook doesn't is because HTML elements have click events; they don't have Vue component lifecycle hooks.

EDIT: To demonstrate, go ahead and visit this Codesandbox, navigate to App.vue and try to move the @hook:mounted directive from the Child component to the div that contains it, you'll notice the hook is never called because div has no life-cycle hooks

EDIT: Also your Vue component needs to look like this:

mounted: function(a) {
    this.myMethod("Hello, I am mounted!");
},
methods: {
  myMethod(a) { console.warn(a); }
},
template: '<div></div>'

Upvotes: 6

Related Questions