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:
-
Implement the
data-nuxt-link
logic suggested by @Atinux into your HTML serializer. -
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
)
}
- 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 ?
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 🔥