PrismJS / prism

Lightweight, robust, elegant syntax highlighting.
https://prismjs.com
MIT License
12.22k stars 1.29k forks source link

PrismJS doesn't highlight with VueJS #1153

Closed Sjoerd closed 7 years ago

Sjoerd commented 7 years ago

Hi, prism does not work when the content is loading async. (As with VueJS)

<pre id="editor">
  <code class="language-css">{{ paste.content }}</code>
</pre>
LeaVerou commented 7 years ago

You can use the API! http://prismjs.com/extending.html#api

Sjoerd commented 6 years ago

That doesn't work :/ (https://gyazo.com/b56294adf1375aed6156ee056da3ad21)

haryratsimba commented 6 years ago

Hello,

someone had a similar issue, using highlightjs which does a similar job as Prism (check the lastest answer from LinusBorg).

I don't know if it's your case, but I guess you are updating your model (paste.content) from your Vue / Component instance and you are expecting the DOM to be updated and highlighted in the same time.

The problem is that Prism will update your model rendered value (raw string), then apply a lot of HTML transformations. Then VueJS won't be able to update the DOM. This is well described in the link I mentionned above :

  • Either it's real HTML DOM elements, and Vue handles them (then {{content}} works),
  • or it's supposed to be highlighted HTML markup, and highlightjs handles them underlying DOM elements that it needs to print it's highlighted text (then {{content}} won't work because highlightJS controls that DOM, not Vue).

One possible dirty solution is to update manually the DOM :

EDIT:

An exemple of code is :

watch: {
  // Note : paste.content is declared as string and assume paste is defined in Vue data
  'paste.content': function(value) {
    let editor = document.querySelector('#editor code');
    editor.innerHTML = value;
    this.$nextTick(()=> Prism.highlightElement(editor));
  }
}

One other option is to watch the paste object using the deep option which will help you to observe inner property change:

watch: {
  // Observe paste instance defined in Vue data using the deep option
  paste:  {
    handler(value) {
      let editor = document.querySelector('#editor code');
      editor.innerHTML = value;
      this.$nextTick(()=> Prism.highlightElement(editor));
    },
    deep: true
  }
}

Hope it will help you

Sjoerd commented 6 years ago

It doesn't work for me, the [dot] between paste and content (paste.content) is unexpected.

haryratsimba commented 6 years ago

Sorry, I bring some corrections. To watch paste inner property, the syntax should be 'paste.content': function(value) Please check the previous edited answer.

Sjoerd commented 6 years ago

@haryratsimba thank You so much! It works :)

LeaVerou commented 6 years ago

I wonder if something like this would help, but where the mutation observer tracks the content of the <code> elements, not just new ones. Then every time Vue updates the <code> element, Prism automatically highlights it.

morissette commented 5 years ago

Solved using updated property on component:

  var Post = {
    data: function() {
      var postData = {
        post: ''
      };   
      var articleTitle = this.$route.path.replace('/', '');
      fetch(getPostUrl + articleTitle, {method: "GET", cache: "default"}).then(
        function(response) { 
          if (!response.ok) {
            console.log("bad response from server");
          }                  
          return response.json()
        }).then(
          function(responseData) {
            postData.post = responseData;
        }).catch(function(err) {
          console.log(err);
        });
      return postData
    },     
    template: `
      <div class="uk-card uk-card-body uk-card-default">
        <article class="uk-article">
          <h1 class="uk-article-title uk-text-center uk-text-uppercase">{{post.title}}</h1>
          <p class="uk-article-metadata uk-text-center" v-show="post.author">Written by {{post.author}} on {{post.timestamp}}</p>
          <hr class="uk-divider-icon" v-show="post.author">
          <p v-html="post.content"></p>
          <div class="uk-text-center">
            <div uk-spinner="ratio: 5" v-show="!post.author"></div>
          </div>
        </article>
      </div>
    `,     
    updated: function() {
      Prism.highlightAll();
    }      
  };  
gntzh commented 4 years ago

@morissette I had the same problem. Do you know why it is ok to use updated property, but watch property isn't?

akhmadsyarif04 commented 3 years ago

Solved using updated property on component:

  var Post = {
    data: function() {
      var postData = {
        post: ''
      };   
      var articleTitle = this.$route.path.replace('/', '');
      fetch(getPostUrl + articleTitle, {method: "GET", cache: "default"}).then(
        function(response) { 
          if (!response.ok) {
            console.log("bad response from server");
          }                  
          return response.json()
        }).then(
          function(responseData) {
            postData.post = responseData;
        }).catch(function(err) {
          console.log(err);
        });
      return postData
    },     
    template: `
      <div class="uk-card uk-card-body uk-card-default">
        <article class="uk-article">
          <h1 class="uk-article-title uk-text-center uk-text-uppercase">{{post.title}}</h1>
          <p class="uk-article-metadata uk-text-center" v-show="post.author">Written by {{post.author}} on {{post.timestamp}}</p>
          <hr class="uk-divider-icon" v-show="post.author">
          <p v-html="post.content"></p>
          <div class="uk-text-center">
            <div uk-spinner="ratio: 5" v-show="!post.author"></div>
          </div>
        </article>
      </div>
    `,     
    updated: function() {
      Prism.highlightAll();
    }      
  };  

thank mr. i try like this :

import prism from "prismjs/prism";

computed: {
    highlightedCode() {
      return this.dataArtikel;
    },
  },
  updated: function () {
    prism.highlightAll();
  },

and my template :

<span v-html="highlightedCode"> </span>

sucsess, highlight code in v-html..

istomyang commented 3 years ago

Sorry I don't read PrismJS src, but I have some experience.

I think {{content}} is damaged when PrismJS handle this line code, which causes vue can't update data.

<pre><code>{{content}}</code></pre>

This is a solution, Vuejs can get <code> all along:

<pre><code class="language-html" v-text="content"></code></pre>
export default {
  computed: {
    content() {
      this.$nextTick( function() { Prism.highlightAll () } )
      // this is async or mutable
      return `<script>console.log('Hey!')<\/script>`
    },
  },
}
quiet-node commented 6 months ago

The quickest and the easiest way is to force refresh the prism component, and one of the easiest way to force refresh a component is providing a unique :key attribute to it.

<pre id="editor" :key="paste.content">
  <code class="language-css">{{ paste.content }}</code>
</pre>