nuxt-modules / prismic

Easily connect your Nuxt.js application to your content hosted on Prismic

Home Page:https://prismic.nuxtjs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

adrianocr opened this issue · comments

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.

i have exact same problem. Have you fixed it?

@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.

Thanks very much that worked.

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

@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.

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).

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

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

@johndigital's solution worked for me!

@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.

I guess maybe this can be documented @lihbr ?

commented

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

Looking forward to native support for it 🔥