kontent-rich-text-for-vue
v2.0.0
Published
A component for rendering Kentico Kontent rich text elements in vue.js
Downloads
560
Maintainers
Readme
Render rich text from Kentico Kontent in Vue
This package replaces the resolveHtml
method provided by the Kontent Delivery SDK to display value of rich text elements.
Kentico Kontent allows you to write content in rich text elements, where you can do all sorts of formatting. What's more interesting, you can also compose your content from inline or reusable bits of content, referred to as components and linked items respectively.
When you don't need this package
- You're not using these, you can stop reading and go on with your life.
- You're using components or linked items and happy with the way the Kontent Delivery SDK handles rendering linked items.
- You're not using Kentico Kontent with Vue.
- You're not using Kentico Kontent at all.
When you want components to be components, though...
The richTextResolver
extension point of the SDK does decent job, but I wanted my components to be proper components in the framework I was using.
That happened to be Vue.js at the moment. So I wrote this tiny component that makes some of my dreams come true.
Installation
Pick the package up form npm:
npm install kontent-rich-text-for-vue --save
Basic usage
This package contains a component that accepts the raw rich text value from Kentico Kontent delivery API and your component that will replace each linked item or component found in the text.
Let's say we have content in items of content type blog_post where there are 2 elements:
- title is a regular text that can be rendered as-is
- content is a rich text and needs a bit more love
<template>
<h1>{{blogPost.title.value}}</h1>
<div> <!-- here you would normally put attribute v-html='blogPost.content.resolveHtml()' but no more -->
<rich-text
:content='blogPost.content.value'
:linkedItemComponent='linkedItemComponent'
/>
</div>
</template>
<script>
import { RichText } from 'kontent-rich-text-for-vue';
import LinkedItem from './components/linked-item.vue';
export default {
components: { RichText },
computed: {
blogPost: () => blogPost, // Pick it up from a vuex store or wherever you happen to keep it
linkedItemComponent: () => LinkedItem
}
}
</script>
Now, my linked-item
component will need to decide how various linked items and components appearing in the text should look like.
To make pairing of linked item components with linked item data easier, one more thing is exported from this package.
<script>
import { linkedItemFactory } from 'kontent-rich-text-for-vue';
import YoutubeVideo from './youtube-video.vue';
import Quote from './quote.vue';
const selectComponent = (contentTypeCodeName) => {
switch (contentTypeCodeName) {
case 'youtube_video':
return YoutubeVideo;
case 'quote':
return Quote;
}
};
export default {
functional: true,
render: (createElement, context) => {
const {props} = context;
const selectLinkedItemData = (itemCodeName) => {
// Again, pick the blogPost from a vuex store or wherever.
// I ended up having page 'provide' the blogPost and then injected it here.
return blogPost.linkedItems[itemCodeName];
};
const component = linkedItemFactory(selectComponent, selectLinkedItemData);
return createElement(component, {props})
}
}
</script>
With this rather complicated setup out of the way, we can start writing components for individual linked item types.
The rich-text component passes to the linked items one prop and that is the item
containing all the linked item data.
In the case of our YouTube video, it contains a video ID and a short description.
<template>
<figure>
<iframe
:src='`https://youtube.com/embed/${item.videoId.value}?rel=0`'
>
</iframe>
<figcaption>{{item.description.value}}</figcaption>
</figure>
</template>
<script>
export default {
props: ['item']
}
</script>
Links
Kontent allows you to add various types of links in your rich text.
Out of these types, the content item link needs its URL resolved to render properly. The link itself contains only ID of the linked content item.
Usually more data, such as codename, content type, or url slug of the particular content item is needed for construction of the URL.
These values can be obtained from the links
field in the rich text element value.
To make this resolution a bit easier, another factory is exported from this package.
However, the factory needs to be injected with the url construction logic, and possibly with customized component to be rendered instead of the plain a
element.
<template>
<h1>{{blogPost.title.value}}</h1>
<div>
<rich-text
:content='blogPost.content.value'
:linkComponent='linkComponent'
/>
</div>
</template>
<script>
import { RichText, linkFactory } from 'kontent-rich-text-for-vue';
import CustomEmailLink from './components/custom-email-link.vue';
export default {
components: { RichText },
computed: {
blogPost: () => blogPost, // Pick it up from a vuex store or wherever you happen to keep it
linkComponent: () => linkFactory({
getLinkComponent: linkType => {
// Can be 'content-item' | 'asset' | 'email' | 'web-url'
if (linkType === 'email') {
return CustomEmailLink;
}
// Otherwise use the default component.
return null;
},
getItemLinkUrl: itemId => {
const {codename, urlSlug, type} = blogPost.content.links.find(link => link.linkId === itemId);
return `https://www.myblog.com/posts/${urlSlug}`;
}
})
}
}
</script>
The linkComponent
is an optional prop and is not necessary unless you need content item link's URLs resolved or link rendering customized in any other way.
Rich text in a rich text
Another content type used as a component or linked item in our example also contains a rich text element quote
.
We'll wrap the value in the same rich-text
component we used on the parent page.
And in case this text contains nested linked items, we'll also pass the linked-item component.
<template>
<blockquote>
<rich-text
:content='item.quote.value'
:linkedItemComponent='linkedItemComponent'
/>
</blockquote>
</template>
<script>
import { RichText } from 'kontent-rich-text-for-vue';
import LinkedItem from './linked-item.vue';
export default {
props: ['item'],
components: {RichText},
computed: {
linkedItemComponent: () => LinkedItem
}
}
</script>
These example components are extremely small and could be easily handled by the out-of-the-box richTextResolver
,
but as the components grew to render more and more of markup, messing with string interpolation became rather obnoxious.
Feedback & Contributions
The provided component is quite bare-bones, but does everything I need. I'm interested if your use case differs and thus my implementation lacks.
Furthermore, I'm no Vue expert, so I'd love to hear if there are nicer or more elegant ways of doing things.