Nested q-layout behavior 0.13 vs 0.14 (again)
-
Hello,
I have been using quasar for a few months and trying to migrate to 0.14 now,
I know that in quasar 0.13 nested layouts can’t be used (there is a topic about this), but there is no problem nesting them as long as I do:
In my App.vue a I have the root layout, it gives the app appearance: right drawer for navigation and header with common actions for the whole app (i.e home, help, logout, and others) and as direct child of this layout element there is a <router-view> for rendering the rest of the components, and each component has it’s own layout, with header and right drawer, so:
The result is that the header in child components, integrates seamlessly with root header and there are two right drawers, the root one which maintains navigation (it’s a tree) and the nested component that has component’s own status, actions etc.
The problem is than I can’t do the same in quasar 2.x, because child layout “overrides” parent one no matter what I do.
The only way to do that is repeating again the “common” layout en each of the components layout but this means loosing the refactoring benefits (mandatory in big SPA applications with many components) of the first approach and of course loosing the navigation state as the navigator (in right panel) is created and destroyed each time a components renders.
Any solution or work-around about this ?
Thanks in advance.
-
I’m using a ‘common’ headerBar.vue as a component that I import in each page.vue component separately.
The advantage is that I can set up the common stuff in the headerbar and override it in a page.vue whenever needed.
Also, gui design/placement remains consistent in all pages.
Maybe this is a way out for you? I’m still on 0.13 but I suppose this will also work in 0.14HeaderBar.vue
<template> <div class="toolbar"> <!-- DEFAULT MENU BUTTON THAT OPENS LEFT DRAWER --> <slot name="btn1left"> <button class="hide-on-drawer-visible" style="width:30px; padding:0;" @click="openLeftSideBar()"> <i>menu</i> </button> </slot> <!-- BUTTON 2 LEFT --> <slot name="btn2left"></slot> <!-- TITLE --> <q-toolbar-title> <div class="toolbarTitle"> <slot name="title"></slot> </div> <div class="toolbarSubTitle"> <slot name="subTitle"></slot> </div> </q-toolbar-title> <!-- BUTTON 5 RIGHT WITH A DEFAULT method--> <slot name="btn5right"> <button-circular size="small" icon="settings" :onClick="openCollectionList"></button-circular> </slot> <slot name="btn4right"></slot> <slot name="btn3right"></slot> <slot name="btn2right"></slot> <!-- BUTTON 1 RIGHT WITH A DEFAULT login.vue button component--> <slot name="btn1right"> <login></login> </slot> </div> </template>
Page.vue
<template> <q-layout> <header-bar slot="header"> <div slot="btn1left> <!-- OVERRIDE LEFT DRAWER BUTTON WITH SOMETHING ELSE --> <button @click="doSomethingElseHere()> myLocalButton</button> </div> <div slot="title"> {{t.skills}}</div> <div slot="subTitle">{{subTitle}}</div> <div slot="btn4right"></div> ...
<script> import HeaderBar from '../common/HeaderBar.vue' ...
export default { components : { HeaderBar, ...
-
@Martin , thanks for sharing this Martin. Definitely a solid approach.
I’m doing something different where I have a “configurable headerbar” which is part of the main layout, so the individual pages does not include it again, but reconfigure the mainbar.
That means child to parent communication. Although a redux store would be a great way to make this manageable, I’m using a simple mixin.
So, a child page can dedice that e.g. no back button is needed under certain page specific conditions, so the reconfig that is needed towards the header bar is not only a matter of setting scalar props but can mean also passing a lamdba expression.
e.g. (page specific code for configuring the header bar)this.resetAll() this.setPrimaryBtnLamdba(() => this.saveItem()) this.setPrimaryBtnIsDisabledLambda(() => this.$v.item.$invalid || JSON.stringify(this.item) === JSON.stringify(this.itemBackup)) this.setPrimaryBtnCaption('save') this.setSecondaryBtnLamdba(() => this.undoChanges()) this.setSecondaryBtnIsDisabledLambda(() => JSON.stringify(this.item) === JSON.stringify(this.itemBackup)) this.setSecondaryBtnCaption('cancel') this.setBackBtnIsVisibleLamdba(item => JSON.stringify(this.item) === JSON.stringify(this.itemBackup))
so, these methods are available thanks to the toolbar-mixin.
The last one as an example (setBackBtnIsVisibleLamdba): the toolbars back button is only visible when the current item on this screen isn’t ‘touched’
setSecondaryBtnLamdba --> is the cancel button where a click will execute my page speciific undoChanges method.Quite flexible, but it works at his best when the general structure of header bar is quite fixed. In many cases that’s a great idea because if gives consistent design.
Cheers
paul. -
Hi Martin, Paul
Thanks for your answer,
Your proposal is one of the solutions I was trying:
The problem a found is that on my refactored SideBar.vue I use other components, and one of them is a navigation tree of every object in the app. The case is that every component that uses SideBar.vue does a life cycle of it, creating then destroying it, so the “ui” of the drilled down tree is loosed every time root component changes.
Another approach I’m trying is similar to Paul’s: in the layout page define a side bar for navigation, main header, and a sub header which is filled with data from <router-view> rendered component (title, buttons…), however some <router-view> components need their own sidebar, and I’m trying to find a good way to integrate them in the layout page. The idea is to have a <q-layout> component with an at least one <router-view> and sub-components (sub-routes) for each component needed, this however couples layout component with its children because: 1. maybe some child ui shoud be placed in layout and 2. child components have to hide side bar when push routes (as said in quasar 0.14 layout documentation)
Cheers.
Manuel