DEV Community

Shaked
Shaked

Posted on • Edited on

Html to render function

Hey, my name is Shaked, and I want to tell you how to convert boring HTML to Vue/react render function.
There is often this issue with legacy content or content you want to load in general, which has an HTML format, and as we all know, neither Vue nor Rack likes to load HTML directly. Both frameworks can do it; for instance, Vue/Nuxt has v-html, which can let you place html inside, but the issue with that is security and bad practice. Also, you are kind of out of luck if you want to modify HTML on the fly; for example, you want to change all A tags to be a nuxt-link tag or change the links that have Twitter inside in the href of the tag to be twitter-component to load your custom component.
So I built a solution; here is the git repo -> will very appreciate if you can start it. Also you can look at the Youtube video
This repository has a couple of files. The first is a python docker container which will help you convert the content. This container is a flask server that runs a miniature python version.
This server/container will start from the app.py file.
There is an HTML example and JSON file to show the result. Also, a Vue file to show you an example of how to use it. Please note that in the Vue example, I am using Vuex to get the content, but you can pass it anyway to like props, etc.
So let's see the code here
app.py

@app.route('/', methods=['POST'])
@cross_origin()
def index(): # html_content is a string 
    html_content = request.form.get('html')
    try:
        html = HtmlConverter()
        html.initWithHtml(html_content)

        return {"success": "Successfully got render func.", "data": html.soupToJson() }
    except Exception as e:
        print(e)
        return {"error": "can not convert this to render func" }
Enter fullscreen mode Exit fullscreen mode

So very simple flask server getting the post html parameter and convert it using my custom HtmlConverter object. to more info about this object go to https://github.com/1shaked/render-function-from-html/blob/main/convertor.py

Vue example with render function

<script>
const imgComponent = () => import('./Image')
const videoComponent = () => import('./Video')
const audioComponent = () => import('./Audio')
const youtubeComponent = () => import('./Youtube')
const twitterComponent = () => import('./Twitter')
const facebookComponent = () => import('./Facebook')
const taboolaComponent = () => import('~/components/taboola')
export default {
  components: {
    'img-component': imgComponent,
    'video-component': videoComponent,
    'audio-component': audioComponent,
    'youtube-component': youtubeComponent,
    'twitter-component': twitterComponent,
    'facebook-component': facebookComponent,
    'taboola-component': taboolaComponent
  },
  computed: { ...mapState('articles', ['content']) },
  methods: {
    handleContent (content, h) {
      if (typeof content === 'string' || content instanceof String) {
        return content
      }
      if (Array.isArray(content) && content.length > 0) {
        return content.map((el) => {
          // console.log('%c currentElement: ' + el.tag + ' attrs: ' + el.attrs + ' content: ' + el.content, 'color: green; font-weight: bold')
          if (typeof el === 'string' || el instanceof String) {
            return el
          }
          const attrs = { attrs: { ...el.attrs } /* style: { order: '1' } */ }
          if (Array.isArray(el?.content) && el?.content?.length > 0) {
            return h(el.tag, {}, this.handleContent(el.content, h))
          }
          if (
            el.tag === 'iframe' &&
            el.attrs?.src?.includes('www.facebook.com')
          ) {
            return h('facebook-component', { props: { item: el.attrs.src } })
          }
          if (el.tag === 'img') {
            attrs.on = {
              click: (event) => { // add custom event
              }
            }
          }
          return h(el.tag || 'span', attrs, el.content)
        })
      }
      return ''
    }
  },
  render (createElement) {
    const content = [...this.content] // create copy to avoid error in reactive node
    return createElement('div', {}, this.handleContent(content, createElement))
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)