storyblok / storyblok-nuxt

Storyblok Nuxt module
https://www.storyblok.com/tp/nuxt-js-multilanguage-website-tutorial
MIT License
278 stars 44 forks source link

Visual editing not working on first load #881

Open nicwands opened 3 months ago

nicwands commented 3 months ago

Describe the issue you're facing

I am having trouble getting visual editing to work on first load with SSG.

I followed the tutorial here to create a fully static preview environment: https://www.storyblok.com/tp/create-a-preview-environment-for-your-nuxt-3-website

When in the Storyblok editor, visual editing does not work when it's first loaded. If I make any change in the editor though, the bridge correctly updates everything and after that update, visual editing works as expected.

The problem seems to be in the v-editable composable not adding the necessary attributes on first load for some reason. I tried creating my own custom directive and found that binding.value does not have the _editable property needed to add these attributes, which I think means that the bridge is not initialized yet.

Does anyone know why the bridge isn't initializing correctly on first load?

Here is my preview.js:

export default defineNuxtPlugin((nuxtApp) => {
    const route = useRoute()

    const preview = route.query?._storyblok || false

    if (preview) {
        nuxtApp.hook('page:finish', () => {
            console.log('refresh')
            refreshNuxtData()
        })
    }

    return { provide: { preview } }
})

And the composable I'm using to fetch stories:

export const useAsyncStory = async (
    url,
    apiOptions = {},
    bridgeOptions = {}
) => {
    const { $preview } = useNuxtApp()
    const version = $preview ? 'draft' : 'published'
    const refreshKey = ref(url)
    const story = ref()

    onMounted(async () => {
        if (story.value && story.value.id) {
            useStoryblokBridge(
                story.value.id,
                (evStory) => {
                    story.value = evStory
                    refreshKey.value =
                        Math.random() + (1).toString(36).substring(7)
                },
                { resolveLinks: 'url', ...bridgeOptions }
            )
        }
    })

    const { data } = await useAsyncData(url, async () => {
        const { data } = await useStoryblokApi().get(`cdn/stories/${url}`, {
            version,
            resolve_links: 'url',
            resolve_links_level: 2,
            ...apiOptions,
        })

        return data?.story
    })

   if (data.value?.content) {
        story.value = data.value
    } else {
        throw createError({
            statusCode: 404,
            statusMessage: 'Not Found',
            fatal: true,
        })
    }

    return { story, refreshKey }
}

Reproduction

Not possible

Steps to reproduce

No response

System Info

System:
    OS: Linux 6.9 Arch Linux
    CPU: (20) x64 13th Gen Intel(R) Core(TM) i9-13900H
    Memory: 4.64 GB / 15.25 GB
    Container: Yes
    Shell: 5.9 - /usr/bin/zsh
  Binaries:
    Node: 20.9.0 - ~/.nvm/versions/node/v20.9.0/bin/node
    Yarn: 1.22.21 - ~/.nvm/versions/node/v20.10.0/bin/yarn
    npm: 10.1.0 - ~/.nvm/versions/node/v20.9.0/bin/npm
    pnpm: 9.4.0 - /usr/bin/pnpm
  Browsers:
    Chromium: 126.0.6478.182

Used Package Manager

pnpm

Error logs (Optional)

No response

Validations

codeflorist commented 2 months ago

One thing i can think of, is that you are just using url as the key for the useAsyncData call. This means, that on client in the editor, the static payload generated by the server will be used, which was using version: 'published', but it should use version 'draft' in the editor. This also explains, why the v-editable info is empty, since this is not provided by the API with version: 'published'-calls.

Try adding the version to the key:

const { data } = await useAsyncData(url+version, async () => {
...

I think the refreshNuxtData() in the preview.js is intended to serve the same purpose by reloading the async-data (this using the draft version), but i also wasn't able to get this to work. The docs refer to the solution presented in the discussion here. In the newer comments the original author confirmed (in May 2023), that the solution is not working anymore (referring to an alternative solution. So Storyblok's ultimate tutorial seems to be outdated in this case. But in my experience none of this is necessary anyway, if you make sure, the draft version of the story is fetched, if you are in the editor.

Imho Storyblok should revise that part of the tutorial to a currently working solution. Investigating Nuxt's relatively new composable usePreviewMode for this - as proposed by another issue - would probably be a good solution. I've added an issue in the tutorial-repo (https://github.com/storyblok/nuxt-ultimate-tutorial/issues/99).