Skip to content

Render custom vue 3 components #36

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

Open
danielmalmros opened this issue Sep 17, 2021 · 5 comments
Open

Render custom vue 3 components #36

danielmalmros opened this issue Sep 17, 2021 · 5 comments

Comments

@danielmalmros
Copy link

danielmalmros commented Sep 17, 2021

Hi,

Thanks for maintaining this project :)

I'm trying to use "[email protected]" in our Vue 3 (Single Page Application) to render some custom components based on BLOCKS.EMBEDDED_ENTRY.

We have several different embedded entry types in Contentful that all uses different layout - so we need to render different custom components. But right now it seems like thats not possible?

Right now, I can't even render a router-link.
When looking into the DOM, I see <router-link to="/path">Read more</router-link> - so it seems like it's not rendering correctly? I guess I would expect it to render the router-link or any other custom components.

Here is an example of what we are trying to do:

<template>
  <RichTextRenderer
    :document="component.richText"
    :nodeRenderers="renderNodes()"
  />
</template>

<script lang="ts">
import { defineComponent, h } from "vue";
import { BLOCKS } from "@contentful/rich-text-types";
import RichTextRenderer from "contentful-rich-text-vue-renderer";

export default defineComponent({
  name: "Example",
  components: {
    RichTextRenderer,
  },
  props: {
    component
  },
  setup() {

    const renderNodes = () => {
      return {
        [BLOCKS.EMBEDDED_ENTRY]: (node, key, next) =>
          h(
            "router-link",
            {
              key,
              to: 'path'
            },
            'Read more'
          ),
      };
    };

    return { renderNodes };
  },
});
</script>
@tolgap
Copy link
Member

tolgap commented Sep 23, 2021

@danielmalmros thank you for the detailed issue report!

I can reproduce your issue. I just can't seem to figure out what's causing it. It's as if Vue has no idea that vue-router has it's components registered.

Have you done any further debugging in the meantime? It would be amazing if you could pinpoint the issue (and even maybe fix it with a PR 👀)

@danielmalmros
Copy link
Author

@tolgap sorry for late response.
I did a little digging but not much and did not find anything unfortunately..

If I can find the time I'll try dig more into it.

@hjujah
Copy link

hjujah commented Oct 22, 2021

If you are trying to sort out the router-link here is the only way we've managed to sort it out... A bit hacky but it works...

<template>
  <div class="rich-text" v-html="parsedText" />
</template>

<script>
import { INLINES } from '@contentful/rich-text-types'
import { documentToHtmlString } from '@contentful/rich-text-html-renderer'
import _each from 'lodash/each'

export default {
  name: "rich-text",

  props: ["text"],

  mounted() {
    this.links = this.$el.querySelectorAll('.nuxt-link-fake')
    _each(this.links, link => link.addEventListener('click', this.onClick))
  },

  computed: {
    richOptions() {
      return {
        renderNode: {
            [INLINES.HYPERLINK]: (node, next) => {
              let link = `<a href='${node.data.uri}' `
              link += this.isExternalUrl(node.data.uri) ? `target='_blank'` : `class='nuxt-link-fake'`
              link += `>${next(node.content)}`
              link += `</a>`
              return link
            }
        }
      } 
    },

    parsedText() {
        return documentToHtmlString(this.text, this.richOptions)
    },

    websiteUrl() {
        return process.client ? location : new URL(process.env.WEBSITE_URL)
    }
  },

  methods: {
    isExternalUrl(url) {
      return new URL(url).origin !== this.websiteUrl.origin;
    },

    onClick(e) {
      e.preventDefault()
      let url = new URL(e.currentTarget.getAttribute("href"))
      this.$router.push(url.pathname)
    }
  }
}
</script>

@tolgap
Copy link
Member

tolgap commented Dec 28, 2021

I've been looking into this during my holidays. I think I've figured out what is going wrong. Because of the recent changes to Vue 3, where the createElement, aka h function has been refactored to a global function. If you want to render a component in a render function, you need to actually provide the component itself, instead of just the component template name: https://jsfiddle.net/2rbxg1q9/

So actually, there are no changes required in this library. You just need to make sure to actually use the component definition, instead of just it's name.

@tolgap
Copy link
Member

tolgap commented Dec 28, 2021

@danielmalmros could you try out the solution in that fiddle, and let me know if it worked out for you? Then we can close this issue👍.

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

No branches or pull requests

3 participants