How to have q-layout-drawer in a seperate component?



  • I want to have my q-layout-drawer decoupled from my Layout.vue file, meaning moving it to a component.
    You can assume the default quasar page of a vanilla quasar project.
    And the modifications concern basic extraction, like so:
    now having following in my Layout.vue file
    under script:

    import sidenav from './../components/q-layoutdrawer-moved-here.vue';
    export default {
      name:'myLayout',
      components: {sidenav},
    	data () {
    		return {
    			leftDrawerOpen: this.$q.platform.is.desktop
    		}
    	},
    

    and in the template

    <sidenav :leftDrawerOpen=leftDrawerOpen/>
    

    which works… poorly
    I get a warning that I should set up a computed proberty, because I am mutating a passed property for the v-model in the q-layout-drawer.
    Also the drawer behaves not as expected I need click the menue button twice after initial loading of the page, before it does what it should do.

    I’m looking for a solution that does not involve using Vuex and/ or that gets rid of the warning in the console. also an eventbus seems to be a more complicated solution, then it should be, but I’m not sure. …also the wierd doubleclick. …or some explaination about the v-model used on the q-layout-drawer, if I missed some easy solution because of my poor understanding of it.

    Might be a lot I ask for, but maybe someone had this already solved.
    I dont find the documentation for using open() on a ref as suggested here but there also was no v-model on q-drawer 2 years ago (I guess).



  • I found a sufficient solution:
    -> put the q-list in a component (wrap the q-layout-drawer in a v-if element)

    But its just more like a workaround.



  • Well first of all, we need to see the code for your ‘sidenav’ drawer component too. It sounds like you are not passing a prop to it to toggle it (or an event from the parent). Also we’d need to see the html for it in the parent and the code for the button that toggles it.



  • This post is deleted!


  • .sry, not solved. But the system recognized my as spam, so I can’t change the title.
    However, here is the sidenav component:

    <template lang="pug">
        q-layout-drawer(
            v-model="leftDrawerOpen"
            overlay
            :content-class="$q.theme === 'mat' ? 'bg-grey-2' : null")
    
          q-list(
            no-border
            link
            inset-delimiter)
            q-list-header Essential Links
            q-item(@click.native="openURL('http://quasar-framework.org')")
              q-item-side(icon="school")
              q-item-main(label="Docs" sublabel="quasar-framework.org")
    
    </template>
    
    <script>
    import { openURL } from 'quasar'
    
    export default {
        name: 'sidenav',
        props: ['leftDrawerOpen'],
        methods: {
            openURL
        }
    }
    </script>
    


  • Where is the HTML (or PUG) for the sidenav in the layout file?
    Also if you hit F12 in the browser and view the console output, do you see something like:

    Avoid mutating a prop directly since the value will be overwritten whenever the parent
    component re-renders. Instead, use a data or computed property based on the prop's value.
    Prop being mutated: "leftDrawerOpen"
    


  • This post is deleted!


  • I experienced a similar problem with q-layout-drawer and I was seeing the Vue warning: ‘Avoid mutating a property directly…’ .

    As you should know, if you are passing the ‘leftDrawerOpen’ variable down to the child component as a prop then you should avoid manipulating that prop directly in the child component. I was not attempting this in my code but I was still seeing the error.

    It turns out that q-layout-drawer has a built-in function which detects clicks outside the drawer and attempts to close the itself by updating the v-model ‘leftDrawerOpen’. This works fine when the v-model references data, but when it’s referencing a prop it shows the mutation warning because it’s attempting to manipulate the property.

    In order to get around this you have to copy the ‘leftDrawerOpen’ prop variable into a local data variable. This means the q-layout-drawer can manipulate the copied data variable without warnings. However, you have to keep in mind that the ‘leftDrawerOpen’ prop could be changed from the parent or from another component, so to keep our local copy up to date you have to watch for changes to that prop.

    Here is an example:

    /*CustomDrawerComponent.vue*/
    /* Passing local data instead of the prop to q-layout-drawer */
    <q-layout-drawer side="left" v-model="localLeftDrawerOpen">
        <q-list link inset-delimiter>
            <q-item to="/">
                <q-item-side icon="home" />
                <q-item-main label="Home" sublabel="This is your home" />
            </q-item>
        </q-list>
    </q-layout-drawer>
    </template>
    
    <script>
    
    export default {
    
        props: {
            /* this prop gets passed down from our parent - it should NOT be manipulated directly! */
            leftDrawerOpen: Boolean
        },
    
        data () {
       
                return {
                    /* A local data copy of our prop - which CAN be manipulated here */
                    localLeftDrawerOpen: false
        
                }
        
        },
        
        watch: {
            
            /* If our prop ever gets changed outside of this component then we need to update our local data version of the prop */
            leftDrawerOpen: function(newVal) {
                this.localLeftDrawerOpen = newVal;
            }
    
        },
    
        mounted: function() {
    
            /* As soon as the component is mounted convert our passed prop into data*/
           /* This line may or may not be necessary - The watch function probably covers it already, but I haven't tested without it yet */
            this.localLeftDrawerOpen = this.leftDrawerOpen;
    
        }
    
    }
    </script>
    
    /* Layout */
    
    <template>
    <q-layout>
    
        <q-layout-header>
    
            <q-toolbar color="primary" :inverted="$q.theme === 'ios'">
    
                <q-btn flat dense round @click="leftDrawerOpen = !leftDrawerOpen" aria-label="Menu">
                    <q-icon name="menu" />
                </q-btn>
    
                <q-toolbar-title>
                    My Custom App
                    <div slot="subtitle">Running on Quasar v{{ $q.version }}</div>
                </q-toolbar-title>
    
            </q-toolbar>
    
        </q-layout-header>
    
        /*passing our parent's data down to our child as a prop*/ 
        <custom-drawer :left-drawer-open="leftDrawerOpen"></custom-drawer>
    
        <!-- this is where the Pages are injected -->
        <q-page-container>
    
            <router-view></router-view>
    
        </q-page-container>
    
    </q-layout>
    </template>
    
    <script>
    
    //Our custom components
    import CustomDrawer from 'components/CustomDrawer'
    
    export default {
    
        components: {
            CustomDrawer 
        },
    
        // name: 'LayoutName',
        data () {
            return {
                leftDrawerOpen: this.$q.platform.is.desktop
            }
        },
    
        mounted () {
    
            var self = this;
    
        }
    }
    </script>
    
    

    And to go a little further, if you want to keep your header in a separate component then do something like this using Quasar’s global event bus:

    
    /*CustomHeader.vue*/
    /*Emit a request to update the leftDrawerOpen variable*/
    <q-btn flat dense round @click="$root.$emit('toggle_left_drawer')" aria-label="Menu">
         <q-icon name="menu" />
    </q-btn>
    
    
    /*Layout.vue*/
    mounted () {
    
    var self = this;
    
    //List for $emits from any child components
    this.$root.$on('toggle_left_drawer', function() {
          
         /* our parent's data is updated, which in turn updates the sidebars props, which in turn updates the sidebars local data - NO WARNINGS :) */
          self.leftDrawerOpen = !self.leftDrawerOpen;
    
     });
    
    }
    
    ``