nuxt icon indicating copy to clipboard operation
nuxt copied to clipboard

Allow `layout` to be set after `asyncData` has resolved

Open luishdz1010 opened this issue 5 years ago • 15 comments

What problem does this feature solve?

Hi,

I have a heavily dynamic route (pages/_slug/index.vue) where I need to control the layout displayed based on the payload that resolves from asyncData. So basically the server decides what layout to use, from a set of known layouts. I tried to do something like:

layout({ store }) {
     return store.getters.pageFromRoute.layout;
  },

But this won't work since pageFromRoute is populated after asyncData has resolved, the resolving of the layout happens too soon. Is there any technical reason why layouts have to be resolved before asyncData? Previously I had ditched the layout system altogether favoring using a single layout and just using normal components to simulate layouts, but I need animated route transitions and they are hard to implement if using only a single layout (because the whole page is transitioned, menus/footer or other global elements are replaced in each transition, they should be static)

What does the proposed changes look like?

I don't have an API in mind yet, I sorta just expected this to work

This feature request is available on Nuxt community (#c7347)

luishdz1010 avatar Jul 06 '18 21:07 luishdz1010

@luishdz1010 Did you find a workaround?

strebl avatar Sep 14 '18 11:09 strebl

Nope. I took a look at the rendering pipeline and I might have an idea on how to make this happen but it will require me to maintain a fork. Hoping we can get a response from the maintainers before going for that solution.

luishdz1010 avatar Sep 14 '18 15:09 luishdz1010

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

stale[bot] avatar Oct 31 '18 18:10 stale[bot]

This is still a problem in v2

luishdz1010 avatar Oct 31 '18 20:10 luishdz1010

@luishdz1010 if you have a solution, i would imagine the best thing to to would be to create a pull request.

samburgers avatar Jan 06 '19 09:01 samburgers

I'm struggling to work through this issue as well. We can pass context to the layout, but it would be nice if we could also pass something along the lines of a store property to wait for. Such that we could either use a boolean or just confirm that the state is defined (if setting store data after async), before initiating the layout.

Has anyone been able to figure out a clean workaround?

The use of layouts doesn't seem very practical as is for dynamic headless CMS integration.

DigitalHodgePodge avatar Feb 21 '19 18:02 DigitalHodgePodge

This would be an incredible feature for nuxt, being able to choose a layout depending on async data coming form an API or somewhere gives us the possibility to play around with the app and show the user different information/structure for the app.

Has anyone solved this so far with a work around?

oalejandroh avatar Apr 04 '19 23:04 oalejandroh

We're trying out Nuxt as a POC for a headless CMS website. This immediately popped up as a MUST HAVE feature. Giving the marketing team the option in the headless CMS to choose a template, and then fetch that prop through asyncData and change the layout is important for us as well.

altryne avatar Apr 26 '19 22:04 altryne

As a workaround, you can perform an HTTP request in the vue-renderer:ssr:contexthook to determine the layout. Something like this (in nuxt.config.js):

import consola from 'consola'
import ky from 'ky-universal'

export default {
  hooks: {
    'vue-renderer:ssr:context': async (ctx) => {
      const { req } = ctx
      const apiCall = `${host}/api/determine-layout${req.url}`
      try {
        const response = await ky.get(apiCall, { throwHttpErrors: false })
        if (response.status === 200) {
          ctx.nuxt.layout = (await response.json()).layout
        }
      } catch(err) {
        consola.warn(err)
      }
    }
  }
}

galvez avatar Jun 28 '19 01:06 galvez

Actually, using the same technique, you have access to post-asyncData data:

    'vue-renderer:ssr:context': (ctx) => {
      ctx.nuxt.layout = ctx.nuxt.data.layout
    }

galvez avatar Jun 28 '19 01:06 galvez

Actually, upon further testing, none of this worked for me.

What finally worked was having a reusable middleware function in a plugin:

import middleware from '../middleware'
import myMiddleware from './myMiddleware'

middleware['layoutSetter'] = myMiddleware

export function(ctx) {
  if (process.client) {
    myMiddleware(ctx)
  }
}

And then have myMiddleware set ctx.$layout, which can then be accessed as:

export default {
  middleware: 'layoutSetter',
  layout({ $layout }) {
    return $layout
  }
}

galvez avatar Jun 28 '19 04:06 galvez

Hello Galvez. Looks like you have solved the problem that I am trying to solve. Do you have this code in a github please so i can have a closer look. thanks ricky

rickyspires avatar Aug 13 '19 13:08 rickyspires

Hey there, any updates about the issue? Maybe somebody has a solution? Best, Y

yevhenlisovenko avatar Dec 06 '19 16:12 yevhenlisovenko

Hi there,

Sadly it was not designed for this purpose at first, supporting it will need a rewrite and this could be possible only for Nuxt 3.

The good news is that you can by-pass current layouts/ by directly using @nuxt/components dynamic component feature: https://github.com/nuxt/components#dynamic-components (please upgrade Nuxt v2.14.8).

See the demo: https://nuxt-dynamic-layouts.surge.sh

Source code: https://codesandbox.io/s/dynamic-layout-with-nuxt-asyncdata-thyos?file=/pages/_name.vue

Note that for full static, I had to add a layout.client.js plugin since asyncData is mocked in order to force the loading of the layout chunk before going to the next page and avoid any UI flash.

Keep in mind that this is a workaround for Nuxt 2 and I don't recommend using it until we support it natively for Nuxt 3.

Atinux avatar Dec 01 '20 19:12 Atinux

Any updates on this? I have exactly the same problem - the server response contains a flag which I need to use to set the correct layout, how can I do this?

goodpixels avatar Jul 16 '22 21:07 goodpixels

This is now possible in nuxt 3, using <NuxtLayout> within your page.

danielroe avatar Feb 14 '23 19:02 danielroe