Search code examples
vuejs2vuetify.jsvue-cli

Vue manually mounting & remounting components


I have the following stripped down code that dynamically mounts components from a dropdown list:

<template>
  <v-app>
    <v-container>
      <v-layout>
        <v-select label="Providers"
          single-line
          :items="providers"
          item-text="txt"
          item-value="val"
          :v-model="provider"
          v-on:change="setProvider" />
        
        <div ref='provider' id='provider' />
      </v-layout>
    </v-container>
  </v-app>
</template>

<script>

import Provider1 from './components/Provider'
import Provider2 from './components/Provider2'
import Vue from 'vue'
import vuetify from './plugins/vuetify';

export default {
  data: () => {
    return {
      provider: null,
      providers: [
        {txt: 'a', val: Provider1},
        {txt: 'b', val: Provider2}
      ],
    };
  },

  methods: {
    setProvider(val) {
      console.log(this.$refs.provider);

      if (this.provider) {
        // unmount and/or re-create #provider dom element
      }

      this.provider = new (Vue.extend(val))({
        vuetify,
      }).$mount('#provider');
    }
  },
}

</script>

First selection works great, subsequent selections graces my console window with "[Vue warn]: Cannot find element: #provider" What should be placed in // unmount and/or re-create #provider dom element?

Also, if these need to be separately created questions, let me know:

  1. What happens to the dom element? It doesn't get replaced as console.log(this.$refs.provider); clearly shows.
  2. Why is manually mounting components advised against everywhere by everyone? Pending info on the unmount code, this way of doing it looks much more elegant than a slough of v-ifs would look in my opinion. (edit: added 3rd question)
  3. Are there any downsides to mixing vanilla markup with Vuetify's such as the above <div />?

Thanks

(edit: revised, working code. I've added an emit for extra fun)

<template>
  <v-app>
    <v-app-bar app />
    <v-main>
        <v-select label="Providers"
          :items="providers"
          v-model="provider" />
      <component :is="provider" @fb="feedback" />
    </v-main>
  </v-app>
</template>

<script>

import Provider1 from './components/Provider'
import Provider2 from './components/Provider2'

export default {
  data: () => {
    return {
      provider: null,
      providers: [
        {text: 'a', value: Provider1},
        {text: 'b', value: Provider2}
      ],
    };
  },

  methods: {
    feedback(v) {
      alert(v);
    }
  }
}

</script>

Solution

  • If your objective is to change between components on-the-fly, you can use the is Vue keyword to build dynamic components. That way you won't need to use v-ifs to control which component must render.

    I'm also pretty sure you're not supposed to $mount inside components I believe that causes some side-effects and isn't generally good practice, since there are at least other ways to do it.

    About mixing Vuetify and vanilla HTML, there's mostly no problem there. Some of Vuetify's selectors are pretty specific (like using scrollable in a v-dialog with v-card) but most are more general.