Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

document getSSRProps for directives #14568

Closed
danielroe opened this issue Aug 12, 2022 · 24 comments · Fixed by #22627
Closed

document getSSRProps for directives #14568

danielroe opened this issue Aug 12, 2022 · 24 comments · Fixed by #22627

Comments

@danielroe
Copy link
Member

I believe the documentation is missing something or there is a bug about getSSRProps: https://v3.nuxtjs.org/guide/directory-structure/plugins/#vue-directives

When I copy the content and paste it into /plugins/focus.ts or /plugins/focus.client.ts (in a project without any directives just with pages) I get the same getSSRProps missing error as anyone above - but the besides the error, it seems to work on most mounted elements.

(To check, the .focus() changed to .style.color = 'green' did its job)

_Originally posted by @BananaAcid in #13351

@Lyokolux
Copy link

I am currently using a directive to toggle tabIndex based on the focus. It avoids focus trap if two tabindex (one link and one with an attribute tabindex=1) are nested. The code below is used:

import { useFocusWithin } from "@vueuse/core"

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.directive('v-outside-focus-only', {
    mounted(el: HTMLElement) {
      const { focused } = useFocusWithin(el)
      watch(focused, focused => {
        el.tabIndex = focused ? -1 : 0
      })
    },
    getSSRProps(binding, vnode) {
      // you can provide SSR-specific props here
      return {}
    }
  })
})

It returns the error (on version 3.0.0-rc.8):
[nuxt] [request error] [unhandled] [500] Cannot read properties of undefined (reading 'getSSRProps')

@Lyokolux
Copy link

Lyokolux commented Aug 31, 2022

The current function in ./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js is raising this error

function ssrGetDirectiveProps(instance, dir, value, arg, modifiers = {}) {
    if (typeof dir !== 'function' && dir.getSSRProps) {
        return (dir.getSSRProps({
            dir,
            instance,
            value,
            oldValue: undefined,
            arg,
            modifiers
        }, null) || {});
    }
    return {};
}

Here dir is undefined.
The values of the parameters in order are: {} undefined undefined undefined {}.
This happens regardless of the .client suffix.

In the vue source code (https://github.com/vuejs/core/blob/main/packages/server-renderer/src/helpers/ssrGetDirectiveProps.ts#L5), dir is expected to be of type Directive.

Ok.

@Lyokolux
Copy link

Lyokolux commented Aug 31, 2022

It is compiled to

__vite_ssr_import_30__.ssrGetDirectiveProps(_ctx, _directive_outside_focus_only))

in .nuxt/dist/server/server.mjs.

_directive_outside_focus_only is defined as

const _directive_outside_focus_only = __vite_ssr_import_29__.resolveDirective("outside-focus-only");

getSSRProps is also never called. However, this seems to be correct according to the vue 3 documentation on SSR directives: https://vuejs.org/guide/scaling-up/ssr.html#custom-directives

@Lyokolux
Copy link

Lyokolux commented Aug 31, 2022

In the typescript type definition of nuxtApp.vueApp.directive, the directive function expects one parameter (a name only).

@Lyokolux
Copy link

As stated by the vue documentation https://vuejs.org/guide/reusability/custom-directives.html#introduction, vue directives can be imported the same way composables are...

@Lyokolux
Copy link

Lyokolux commented Aug 31, 2022

Turning it into a composable made it works:

import { Directive } from "vue"

type UseVOutsideFocusOnlyReturnType = {
  vOutsideFocusOnly: Directive
}
export const useVOutsideFocusOnly = (): UseVOutsideFocusOnlyReturnType => ({
 // recommend to use the same name in the whole app with a property
  vOutsideFocusOnly: {
    mounted(el: HTMLElement) {
      const { focused } = useFocusWithin(el)
      watch(focused, focused => {
        el.tabIndex = focused ? -1 : 0
      })
    },
    getSSRProps(binding, vnode) {
      // you can provide SSR-specific props here
      return {}
    }
  }
})

and in a <script> tag inside a vue component:

const { vOutsideFocusOnly } = useVOutsideFocusOnly()

and in the template:

<NuxtLink :to="myBeautifulLink" v-outside-focus-only>...</NuxtLink>

It solves my problem in Vue 3 with <script setup>. I don't know how to solve it for Vue 2.7 though.

@danielroe danielroe changed the title I believe the documentation is missing something or there is a bug about getSSRProps: https://v3.nuxtjs.org/guide/directory-structure/plugins/#vue-directives document getSSRProps for directives Sep 14, 2022
@TheSpiderPP
Copy link

TheSpiderPP commented Oct 24, 2022

I'm getting this error when I use the wrong name for the directive in a SFC. So maybe its a matter of proper error handling, telling a dev that the wrong name is used or some error occured which made the directive unloadable? For exampleI've been able to reproduce the error using the below test:

export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.vueApp.directive('click-outside', {
    mounted: (el, binding, vnode) => {},
    beforeUnmount: (el) => {}
  })
})

And in my SFC this works:

<template>
  <div v-click-outside="isOpen = !isOpen"></div>
</template>

But this produces the exact error (note the typo)

<template>
  <div v-click-typo="isOpen = !isOpen"></div>
</template>

Copy link
Member Author

Unfortunately this error is coming from Vue itself and not Nuxt. Would you raise an issue on https://github.com/vuejs/core to improve the error?

@chris-visser
Copy link
Contributor

Oh wauw. Just realized I posted my finding on my Passionate People account. I will repost this issue on the Vue repo. Thanks!

@manniL
Copy link
Member

manniL commented Oct 24, 2022

Oh wauw. Just realized I posted my finding on my Passionate People account. I will repost this issue on the Vue repo. Thanks!

BTW: Feel free to link this issue in the new Vue issue then ☺️

@sr-mothership
Copy link

Encountered the same error following the docs for a simple focus directive.

Only after adding

// nuxt.config.ts
export default defineNuxtConfig({
  vue: {
    compilerOptions: {
      directiveTransforms: {
        focus: () => ({
          props: [],
          needRuntime: true
        })
      }
    }
  }
});

from #13351 once (!) it worked.
Interestingly after removing it afterwards from the config file, the directive still works.

@floatingdino
Copy link

The SSR error is also triggered if you have an unknown directive and a Nuxt layout.
https://stackblitz.com/edit/github-kbax54?file=pages/index.vue

@snowdream
Copy link

I have met the same problem.

export default defineNuxtPlugin((nuxtApp) => {
    nuxtApp.vueApp.directive('focus', {
      mounted (el, { value }) {
        if(value == undefined){
            el.focus()
        }else if(value){
            el.focus()
        }
      },
      updated (el, { value }) {
        if(value == undefined){
            el.focus()
        }else if(value){
            el.focus()
        }
      },
      getSSRProps (binding, vnode) {
        // you can provide SSR-specific props here
        return {}
      }
    })
  })
  

@YouMixx
Copy link

YouMixx commented Jun 26, 2023

How's it going? I have the same problem

@SvenWesterlaken
Copy link

Also here unfortunately still stuck with the problem. I am using a directive or a globally installed plugin to add a directive.

@akumanara
Copy link

This is super annoying. Any updates on that?

@BananaAcid
Copy link

Also here unfortunately still stuck with the problem. I am using a directive or a globally installed plugin to add a directive.

Workaround: add a abc.client.ts with the logic and a stub with abc.server.ts to at least ignore the problem for the moment …

@Kram-V
Copy link

Kram-V commented Dec 16, 2023

@BananaAcid how can we register the plugin for both client and server side? using Nuxt App? TIA

@Kram-V
Copy link

Kram-V commented Dec 16, 2023

I have the same error
500

@Kram-V
Copy link

Kram-V commented Dec 16, 2023

and here's how I implement my registration of my plugin

plugin
plugin-nuxt-config
template

@Kram-V
Copy link

Kram-V commented Dec 16, 2023

sir @danielroe

@Kram-V
Copy link

Kram-V commented Dec 16, 2023

how can we register the plugin for both client/server? sir @danielroe

@BananaAcid
Copy link

@Kram-V just use filename.ts (no .client) to be loaded for both. You may check with if statements inside, it it got loaded on client or server side.

@kalnode
Copy link

kalnode commented Oct 14, 2024

For client-only directive, @BananaAcid 's solution works for me.

Re-cap:

Make your directive:
plugins/my-directive.client.ts // Client-only directive code

Then copy it, rename "client" to "server" and strip out any logic:
plugins/my-directive.server.ts // This file needs to exist, with logic or not

Moreover, Nuxt docs describes this exactly:
https://nuxt.com/docs/guide/directory-structure/plugins#vue-directives

If you register a Vue directive, you must register it on both client and server side unless you are only using it when rendering one side. If the directive only makes sense from a client side, you can always move it to ~/plugins/my-directive.client.ts and provide a 'stub' directive for the server in ~/plugins/my-directive.server.ts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.