paramander / contentful-rich-text-vue-renderer

Render Contentful Rich Text field using Vue
https://www.npmjs.com/package/contentful-rich-text-vue-renderer
MIT License
38 stars 12 forks source link

Async support for renderer methods #31

Closed LionelPaulus closed 2 years ago

LionelPaulus commented 3 years ago

To start, thank you for maintaining this module! Really useful 🙏

I tried to use async methods inside a renderer method and it didn't work (returned undefined). Do you think it would be possible to support this?

Example:

<script>
export default {
  methods: {
    async renderMarks() {
      const response = await myAsyncMethod()
      return {
        [MARKS.BOLD]: (text, key, h) => h('custom-bold', { key: key }, text)
      };
    };
  }
}
</script>
tolgap commented 3 years ago

@LionelPaulus thanks for the kind words 🎉 .

Just to be clear, your textual explanation of what you want, seems to differ from what the code is trying to do. The async part of your code is not inside a renderer method, but inside a method that sets up the renderMarks.

The reason I ask is: your code example can be fixed without changing this library.

If the example was something like:

<script>
export default {
  methods: {
    renderMarks() {
      return {
        [MARKS.BOLD]: async (text, key, h) => {
          const response = await myAsyncMethod();
          return h('custom-bold', { key: key }, text)
        }
      };
    };
  }
}
</script>

that will not work as we do not await any promises during rendering. Any promises that needs to be resolved during rendering of nodes or marks, should be lifted to before mounting the RichText component of this library.

LionelPaulus commented 3 years ago

Sorry for the confusion! Indeed your example is what I'm looking to do. Is there no way to wait for that async call before returning the rendering output?

To give you more context, I'm trying to keep my <RichText /> component as generic as possible as it will be used in different pages and with different rendering methods. That's why I would like it to not manage the rendering methods. I would like to move them to the parent component (the page).

Here is what I tried: triggering an emit event so that the parent component can do the rendering and then, using the callback, send back the output to the RichText component. Maybe you have another way of achieving this?

<script>
export default {
  methods: {
    async customEmbeddedEntry (node, key, h) {
      await this.$emit('customEmbeddedEntry', { node, key, h }, (output) => {
        return output
        // output: h('MyComponent', { key: key, to: 'link to embedded entry' }, 'content')
      })
    },
  }
}
</script>
tolgap commented 3 years ago

@LionelPaulus it is not possible to perform async tasks within the render functions. The entire component is a functional Vue component, and it does not allow to return promises from the render function.

I would suggest you actually build up your nodeRenderers and markRenderers on demand in an async function in the parent components. This "building up" could be abstracted to its own function to achieve modularity and genericness.

LionelPaulus commented 3 years ago

Alright, thanks for the explanation :)

I tried your suggestion and moved my nodeRenderers and markRenderers to the parent component but then I'm facing a new issue: the custom components called by customEmbeddedEntry are not registered in my generic <RichText /> component and so, are not showing. Do you have an idea for a solution? Is there a way to dynamically import components when needed for rendering?

tolgap commented 2 years ago

Closing due to non-library specific issue.