pc035860 / angular-highlightjs

AngularJS directive for syntax highlighting with highlight.js
http://pc035860.github.io/angular-highlightjs/example/
MIT License
294 stars 53 forks source link

Not working with dynamically pulled data #87

Open relentless-coder opened 6 years ago

relentless-coder commented 6 years ago

Hi, I have been trying to make this work for a while now.

First of all, content is a blog post, and it is a mixed content. So there are a few p blocks, and then pre code blocks. I have manipulated the data and placed hljs directive to the code tag, but it isn't working for me.

    postSingleFactory.getPost($stateParams.url).then((data) => {
      ctrl.post = data.data.post;
      ctrl.post.comments.forEach(el => el.replyVisible = false);
      ctrl.post.content = ctrl.post.content.replace('<pre class="language-markup">', '<pre>');      
      ctrl.post.content = ctrl.post.content.replace('<code>', '<code hljs hljs-language="js">');
      console.log(ctrl.post.content);
    })

  ctrl.trustThis = (html)=>{
    return $sce.trustAsHtml(html);
  }

and here is how I am using it

    <div class="single_post_content" ng-bind-html="sm.trustThis(sm.post.content)">

As i can see by inspecting my page that the code block contains the two directives, but it isn't working. There are no errors, the css is loading properly too.

Why would this be happening?

pc035860 commented 6 years ago

According your description, you're actually required to have post.content compiled by AngularJS again for directive (like hljs) to work.

// In your controller, don't forget to inject `$templateCache`
$templateCache.put('postContent-xxx', ctrl.post.content);
<!-- use `ng-include` to render post content from `$templateCache` -->
<div class="single_post_content" ng-include="'postContent-xxx'"></div>

Another options is to write a new directive specialized for displaying post.content's HTML code, as well as adding code highlightling with highlight.js.

angular.module('myApp')
.directive('singlePostContent', function (hljsService, $window) {
  return {
    restrict: 'A',
    link: function (scope, iElm, iAttrs) {
      scope.$watch(iAttrs.singlePostContent, function (content) {
        if (content) {
          iElm.html(content);

          // You don't even require angular-highlightjs for this
          var service = $window.hljs || hljsService;
          service.highlightBlock(iElm[0]);
        }
        else {
          iElm.html('');
        }
      });
    }
  };
  };
});
<div class="single_post_content" single-post-content="sm.post.content"></div>

Here's a working demo on Plunker: http://plnkr.co/edit/ZfB4tamhQIOLx0z9WUMH?p=preview

relentless-coder commented 6 years ago

Hi, for some weird reason. the directive highlighted the whole content, even though the part that's not code

pc035860 commented 6 years ago

Try replacing the highlightBlock call on <pre> or <code> elements.

var service = $window.hljs || hljsService;

service.highlightBlock(iElm.find('pre > code')[0]);

// or

service.highlightBlock(iElm.find('pre')[0]);
pc035860 commented 6 years ago

I've update the plunk with iElm.find('pre')[0], and it seems to work correctly now.

http://plnkr.co/edit/ZfB4tamhQIOLx0z9WUMH?p=preview

relentless-coder commented 6 years ago

@pc035860 Hey, it still doesn't work. There was an issue from my end too. I write a post via TinyMCE editor, and when I write code with its inbuilt code tool, it adds an additional class to the pre tag.

I removed that in the following manner:

ctrl.post.content = ctrl.post.content.replace(/<pre class="language-javascript">/g, '<pre>')

And then, it just styles the outer block. it isn't highlighting anything. So I inspected the element and found out that no language class has been added to my code tag. So, I manually added javascript, just to test things out, but it still didn't work.

What could be the reason for this?

pc035860 commented 6 years ago

Can you make a demo of your problem with plunker or something similar? I can't figure out why it's not working with current available information.

relentless-coder commented 6 years ago

@pc035860 Hey, so I made it work with a few tweaks, one of them really bizarre to me. First, I stored the post's content in a scope variable and passed that to the directive, like this

ctrl.content = ctrl.post.content;

and then plugged that into the directive:

<div class="single_post_content" single-post-content="sm.content">

and it worked. I don't know why. The next issue was that it was only working for the first code block, so I changed it to this:

          let codeBlocks = element.find('code');
          let service = $window.hljs || hljsService;
          for(let key in codeBlocks){
            if(codeBlocks[key]){
              service.highlightBlock(codeBlocks[key]);              
            }
          }

And this works, but I get an error in the console

TypeError: e is undefined

Now, i can ignore that, but I really want to keep it clean.

Long story short, I made it work, but I don't know how.