Home > front end >  Vue js Vuetify custom component not rendered based on the display breakpoint
Vue js Vuetify custom component not rendered based on the display breakpoint

Time:01-27

I have a custom component that basically is a v-btn component with specific style. When I'm using it inside v-menu activator with conditional based on display breakpoint, the custom component does not display on the screen. But if I use regular v-btn the button displays properly based on the display breakpoint. What am I doing wrong here?

https://codepen.io/jgunawan-dc/pen/XWzJqRy?editors=1010

<div id="app">
  <v-app id="inspire">
    <div >
      <v-menu offset-y>
        <template v-slot:activator="{ on, attrs }">
          <global-custom-button
            v-if="$vuetify.breakpoint.mdAndDown"
            v-bind="attrs"
            v-on="on"
          >
            Show on medium and lower
          </global-custom-button>
          <v-btn v-else
            color="primary"
            dark
            v-bind="attrs"
            v-on="on"
          >
            Dropdown
          </v-btn>
        </template>
        <v-list>
          <v-list-item
            v-for="(item, index) in items"
            :key="index"
          >
            <v-list-item-title>{{ item.title }}</v-list-item-title>
          </v-list-item>
        </v-list>
      </v-menu>
    </div>
  </v-app>
</div>
Vue.component('global-custom-button', {
  template: '<v-btn outlined color="info" @click="$emit(\'click\', $event)"><slot></slot></v-btn>'
});
new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data: () => ({
    items: [
      { title: 'Click Me' },
      { title: 'Click Me' },
      { title: 'Click Me' },
      { title: 'Click Me 2' },
    ],
  }),
})

CodePudding user response:

The are two errors arising from the above scenario:

Error message 1:

[Vue warn]: Error in nextTick: "NotFoundError: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node."

Error message 2:

DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.

This arises when Vue tries to insert an element before another one, but the element no longer exists in the DOM.

In your case, it seams that changing $vuetify.breakpoint.mdAndDown from true to false or vice-versa cleans the global-custom-button component or the Dropdown v-btn.

One possible workaround would be to use v-show instead of v-if.

A caveat:

Note that v-show doesn’t support the element, nor does it work with v-else.

So this suggestion can work (you can change to fit your needs):

      <global-custom-button
        v-show="$vuetify.breakpoint.mdAndDown"
        v-bind="attrs"
        v-on="on"
      >
        Show on medium and lower
      </global-custom-button>
      <v-btn v-show="!$vuetify.breakpoint.mdAndDown"
        color="primary"
        dark
        v-bind="attrs"
        v-on="on"
      >
        Dropdown
      </v-btn>

Check here

CodePudding user response:

For a quick fix, use v-show as @Rotiken said.

As another solution you could also register two global components and show/hide it using extra condition prop:

<v-menu offset-y>
  <template v-slot:activator="{ on, attrs }">
    <global-custom-button
      :condition="$vuetify.breakpoint.mdAndDown"
      v-bind="attrs"
      v-on="on"      
    >
      Show on medium and lower
    </global-custom-button>
          
    <global-custom-button-2
      :condition="!$vuetify.breakpoint.mdAndDown"
      v-bind="attrs"
      v-on="on"
    >
      Dropdown
    </global-custom-button-2>
  </template>
  ...
</v-menu>

...

Vue.component('global-custom-button', {
  template: '<v-btn v-if="condition" outlined color="info" @click="$emit(\'click\', $event)"><slot></slot></v-btn>',
  props: ['condition']
});

Vue.component('global-custom-button-2', {
  template: '<v-btn v-if="condition" color="primary" dark @click="$emit(\'click\', $event)"><slot></slot></v-btn>',
  props: ['condition']
});
...

According to this CodePen, this also works.


Why this happens and why v-show works fine with same template: I can't give an exact answer, but there are some assumptions.

Vue.js docs says that v-if removes objects from DOM, whereas v-show changes its display state in CSS, but keep it in DOM.

Since you are using v-if in a vuetify v-menu component, perhaps the component has some methods for updating the v-slot:activator content that can conflict (and not performed at the same time) with conditional rendering using v-if and vuetify display breakpoints.

If you are familiar with TypeScript, you can look into v-menu sources or into activatable mixin sources. Maybe here you will find the true reason for this behavior.

If you just want to avoid such problems, use v-show in such cases.

  •  Tags:  
  • Related