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

The prismic nuxt tutorial/guide has a sample html serializer that includes nuxt-link. This isn't getting transformed into an actual link #60

Closed
adrianocr opened this issue Feb 21, 2020 · 13 comments
Labels
bug Something isn't working

Comments

@adrianocr
Copy link

adrianocr commented Feb 21, 2020

If you go over to https://prismic.io/docs/vuejs/getting-started/prismic-nuxt you'll see that they have the following sample serializer:

import linkResolver from "./link-resolver"
import prismicDOM from 'prismic-dom'

const Elements = prismicDOM.RichText.Elements

export default function (type, element, content, children) {
  // Generate links to Prismic Documents as <router-link> components
  // Present by default, it is recommended to keep this
  if (type === Elements.hyperlink) {
    let result = ''
    const url = prismicDOM.Link.url(element.data, linkResolver)

    if (element.data.link_type === 'Document') {
      result = `<nuxt-link to="${url}">${content}</nuxt-link>`
    } else {
      const target = element.data.target ? `target="'${element.data.target}'" rel="noopener"` : ''
      result = `<a href="${url}" ${target}>${content}</a>`
    }
    return result
  }

  // If the image is also a link to a Prismic Document, it will return a <router-link> component
  // Present by default, it is recommended to keep this
  if (type === Elements.image) {
    let result = `<img src="${element.url}" alt="${element.alt || ''}" copyright="${element.copyright || ''}">`

    if (element.linkTo) {
      const url = prismicDOM.Link.url(element.linkTo, linkResolver)

      if (element.linkTo.link_type === 'Document') {
        result = `<nuxt-link to="${url}">${result}</nuxt-link>`
      } else {
        const target = element.linkTo.target ? `target="${element.linkTo.target}" rel="noopener"` : ''
        result = `<a href="${url}" ${target}>${result}</a>`
      }
    }
    const wrapperClassList = [element.label || '', 'block-img']
    result = `<p class="${wrapperClassList.join(' ')}">${result}</p>`
    return result
  }

  // Return null to stick with the default behavior for everything else
  return null
}

Which I have placed in /app/prismic/html-serializer.js

This works perfectly except <nuxt-link>. It doesn't get transformed/rendered. If I convert it to a standard a tag it works fine, but obviously without handling links internally by the router. So is there a way to get this to work? I have also tried and but neither work.

@KristianJohansenVakwerk

i have exact same problem. Have you fixed it?

@adrianocr
Copy link
Author

@KristianJohansenVakwerk I placed a little hack in to make it work.

Change <nuxt-link> to a standard a tag with a class such as <a class="linkHandler" href="${url}">${content}</a>

Then in your code (probably in mounted) add an event handler on all .linkHandler links, set them to event.preventDefault(), and manually handle the link with vue-router like this.$router.push(event.target.pathname)

Obviously the above is a bit of pseudo code. If you can't figure it out let me know and tomorrow or Friday I'll copy/paste what I did.

@KristianJohansenVakwerk

Thanks very much that worked.

@itpropro
Copy link

itpropro commented Mar 1, 2020

You can also use the html serializer directly provided by prismic for nuxt. This one uses the nuxt-link tag:

import linkResolver from "./link-resolver"
import prismicDOM from 'prismic-dom'

const Elements = prismicDOM.RichText.Elements

export default function (type, element, content, children) {
  // Generate links to Prismic Documents as <router-link> components
  // Present by default, it is recommended to keep this
  if (type === Elements.hyperlink) {
    let result = ''
    const url = prismicDOM.Link.url(element.data, linkResolver)

    if (element.data.link_type === 'Document') {
      result = `<nuxt-link to="${url}">${content}</nuxt-link>`
    } else {
      const target = element.data.target ? `target="'${element.data.target}'" rel="noopener"` : ''
      result = `<a href="${url}" ${target}>${content}</a>`
    }
    return result
  }

  // If the image is also a link to a Prismic Document, it will return a <router-link> component
  // Present by default, it is recommended to keep this
  if (type === Elements.image) {
    let result = `<img src="${element.url}" alt="${element.alt || ''}" copyright="${element.copyright || ''}">`

    if (element.linkTo) {
      const url = prismicDOM.Link.url(element.linkTo, linkResolver)

      if (element.linkTo.link_type === 'Document') {
        result = `<nuxt-link to="${url}">${result}</nuxt-link>`
      } else {
        const target = element.linkTo.target ? `target="${element.linkTo.target}" rel="noopener"` : ''
        result = `<a href="${url}" ${target}>${result}</a>`
      }
    }
    const wrapperClassList = [element.label || '', 'block-img']
    result = `<p class="${wrapperClassList.join(' ')}">${result}</p>`
    return result
  }

  // Return null to stick with the default behavior for everything else
  return null
}

Source: Using Prismic with Nuxt.js

@adrianocr
Copy link
Author

@itpropro it's the same one. If you see the link I included at the very top of the post, it's the exact same link you included at the bottom of yours.

@atinux
Copy link
Contributor

atinux commented Mar 10, 2020

Hi @adrianocr

Thank you for the bug report, indeed since we don't use the Vue runtime compiler since it does not support SSR, the <nuxt-link> won't be processed by Nuxt.js.

Instead, the way to go is to do something like you did, I suggest to add a property like data-nuxt-link:

if (element.data.link_type === 'Document') {
  result = `<a href="${url}" data-nuxt-link>${content}</a>`
} else {
  const target = element.data.target ? `target="'${element.data.target}'" rel="noopener"` : ''
  result = `<a href="${url}" ${target}>${content}</a>`
}

Then you can add your listeners based on the a[data-nuxt-link], we actually use something like this for nuxtjs.org: https://github.com/nuxt/nuxtjs.org/blob/master/components/commons/HtmlParser.global.vue

You can use a mixin to share it across your components (until we find something better for it).

@johndigital
Copy link

Thank you for your input @atinux! This thread has been very helpful for me.

Rather than using a mixin to do this at the component level, I found it more useful to wrap this solution into a clientside plugin that will handle it for all cases globally. The full solution becomes:

  1. Implement the data-nuxt-link logic suggested by @Atinux into your HTML serializer.

  2. Add this file to plugins/prismicLinks.js

export default async ({ redirect }) => {
    window.addEventListener(
        'click',
        (event) => {
            // If the clicked element doesn't have the right selector, bail
            if (!event.target.matches('a[data-nuxt-link]')) return

            // Don't follow the link
            event.preventDefault()

            // Push link destination to router
            redirect(event.target.pathname)
        },
        false
    )
}
  1. Add the plugin to your nuxt.config.js with ssr set to false:
{
    // ...other configuration
    plugins: [
        // ...other plugins
        { src: '~/plugins/prismicLinks', ssr: false }
    ]
}

For anyone that finds this useful, I've put a more full example including the htmlSerializer code here: https://gist.github.com/johndigital/21b04f00abca2dca35595289fd51e680

@atinux
Copy link
Contributor

atinux commented Jul 20, 2020

To add more context, you can also use this module https://github.com/daliborgogic/nuxt-interpolation

@incraigulous
Copy link

@johndigital's solution worked for me!

@SaFrMo
Copy link

SaFrMo commented Oct 9, 2020

@johndigital 's answer was extremely helpful! I ran into a situation where I needed to account for query strings and hashes and ended up switching

redirect(event.target.pathname)

to

redirect(`${event.target.pathname}${event.target.search}${event.target.hash}`)

which solves the issue on my end.

Copy link
Contributor

atinux commented Feb 6, 2021

I guess maybe this can be documented @lihbr ?

@lihbr
Copy link
Collaborator

lihbr commented Feb 6, 2021

I'll have a look into this yup + looking forward to fix that with next major of our Vue kit

Copy link
Contributor

atinux commented Feb 6, 2021

Looking forward to native support for it 🔥

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

8 participants