Brett Fisher
Brett Fisher

Reputation: 612

Syntax highlighting on dynamic blog posts not working

I'm making a blog using Vue and am using vue-highlightjs to add syntax highlighting to the code I will write in the blog posts. I'm just using a textarea in my admin panel to write the blog posts, so I have to manually type in the HTML I want displayed.

<textarea v-model="editorData" cols="60" rows="10"></textarea>

editorData is just a String. On the page where the blog posts are displayed, I grab the blog post from the server and display it in a BlogPost.vue component. Here is the code for that component:

<template>
  <div>
    <pre v-highlightjs><code class="javascript">const s = new Date().toString()</code></pre>
    <h1 class="page-title">{{postTitle}}</h1>
    <div v-html="postBody"></div>
  </div>
</template>

<script>
import axios from "axios";
import { baseUrl } from "@/strings";
export default {
  name: "BlogPost",
  data: function() {
    return {
      postTitle: "",
      postBody: ""
    };
  },

  beforeRouteEnter(to, from, next) {
    axios.get(baseUrl + "/blogPosts/" + to.params.id).then(response => {
      next(vm => vm.setData(response.data));
    });
  },
  methods: {
    setData(data) {
      this.postTitle = data.title;
      this.postBody = data.content;
    }
  }
};
</script>

The v-highlightjs directive in the pre tag at the beginning of the template just tells the vue-highlightjs plugin to add syntax highlighting to the code inside.

The problem is that the hardcoded code in the pre tag at the beginning of the div is highlighted, but the dynamically loaded code in postBody is not highlighted. For example, if I type <pre v-highlightjs><code class="javascript">const s = new Date().toString()</code></pre> into my admin panel textarea then display the post in my BlogPost.vue page, it looks like this:

enter image description here

I don't know if vue-highlightjs doesn't highlight the code because it's dynamic or what. Any ideas? Thank you in advance.

P.S. There's got to be a better way to create an admin panel where I can make blog posts that will show up with syntax highlighting when I type code. I tried CKEditor for a bit but found it really confusing. Any suggestions?

Upvotes: 1

Views: 2101

Answers (1)

Brian Lee
Brian Lee

Reputation: 18187

The directive will not highlight updated code after the initial highlighting. To do so, you need to pass a variable to the v-highlightjs directive:

<pre v-highlightjs="postBody"><code class="javascript"></code></pre>

From Vue.js Syntax Highlighting with Highlight.js:

Reacting to code updates
highlight.js replaces the content of the code block. If using the directive as shown above, updating the source-code after the initial highlighting does not work anymore. To be able to update the code and highlight it again after an update, pass the variable directly into the v-highlightjs directive

Here's a working jsFiddle modified from this example.

If you need more control over the content that is highlighted, I would suggest using the highlightjs library itself rather than the directive and manually call hljs.highlightBlock.

new Vue({
  el: '#app',
  data: () => ({
    post: null,
    posts: [
      `Phasellus luctus magna non sem fermentum, sed pulvinar ex blandit. Vestibulum id auctor neque. Etiam aliquam commodo sollicitudin. <pre><code class="javascript">var foo = bar</code></pre>Etiam cursus commodo neque, at semper dui vestibulum auctor.`,
      `Etiam non elit velit. <pre><code class="javascript">while (true) { console.log('test') }</code></pre>Vestibulum nec posuere lorem. Ut dolor ante, eleifend ut porttitor eu, faucibus non sapien.`,
      `Morbi eleifend ornare felis, vel vulputate nibh suscipit id. <pre><code class="javascript">const func = () = ({ foo: 'bar' })</code></pre>Phasellus luctus magna non sem fermentum, sed pulvinar ex blandit. Vestibulum id auctor neque. Etiam aliquam commodo sollicitudin.`
    ]
  }),
  beforeMount() {
    this.post = this.posts[0]
  },
  mounted() {
    this.highlightPost()
  },
  methods: {
    highlightPost() {
      document.querySelectorAll('code').forEach((block) => {
        hljs.highlightBlock(block)
      })
    },
    cycle() {
      const index = this.posts.indexOf(this.post)

      this.post = index === this.posts.length - 1 ? this.posts[0] : this.posts[index + 1]

      this.$nextTick(() => {
        this.highlightPost()
      })
    }
  }
})
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <button @click="cycle">Next Post</button>
  <p id="post-content" v-html="post"></p>
</div>

Upvotes: 2

Related Questions