2

I'm trying to use Highlight JS in my Angular 2 app, and am having some trouble figuring out how to use it, when the codeblock is not static information. What I mean is, the code string comes from the server through an http get call.

So, if I have this:

export  class   WorkstationComponent implements OnInit  {

    private submission = new Submission();
    private criteria = new Array<ExerciseCriteria>();
    private codeString:string = `
        /* HelloWorld.java
        */

        public class HelloWorld
        {
            public static void main(String[] args) {
                System.out.println("Hello World!");
            }
        }`;

    constructor(private _router:Router, private submissionService:SubmissionService,
                private activatedRoute:ActivatedRoute){}

    @ViewChild('code') codeElement: ElementRef;

ngOnInit(){
    this.activatedRoute.params
        // (+) converts string 'id' to a number
        .switchMap((params: Params) => this.submissionService.getSubmission(+params['id']))
        .subscribe(data => this.success(data),
                    error => this.fail(error));

}

success(data:any){
    this.submission = data;
    this.criteria = data.criteria;
    hljs.highlightBlock(this.codeElement.nativeElement);
}

There is no problem...

success

However, if i change to

success(data:any){
    this.submission = data;
    this.criteria = data.criteria;
    this.codeString = data.code;
    hljs.highlightBlock(this.codeElement.nativeElement);
}

I get this:

fail

What am I doing wrong? The HTML is the same

                <pre>
                    <code #code highlight class="java">
                        {{codeString}}
                    </code>
                </pre>
1
  • 2
    My guess is highlightBlock is getting fire before codeString binding gets evaluated. You might need to wait for next tick and call highlightBlock function. Like by doing setTimeout(() => { hljs.highlightBlock(this.codeElement.nativeElement); },0); instead of hljs.highlightBlock(this.codeElement.nativeElement); Commented Nov 19, 2016 at 13:57

3 Answers 3

2

It is happening because highlightBlock function is getting fire before the inner code element content isn't available. One of the solution to do is setTimeout to make apply highlightBlock one tick later once all binding gets applied. But unnecessarily that will make run another change detection.

Better rather than waiting for inner content to bounded on DOM, you could manually applied it to textContent of DOM and then apply highlightBlock function.

Code

success(data:any){
    this.submission = data;
    this.criteria = data.criteria;
    this.codeString = data.code;
    //making sure code is populated by below line
    this.codeElement.nativeElement.textContent = data.code;
    hljs.highlightBlock(this.codeElement.nativeElement);
}
Sign up to request clarification or add additional context in comments.

4 Comments

The setTimeout approach worked for me. Getting error "this.codeElement.nativeElement.textContent is not a function" with the second approach.
@ManuelZamith Ahh.. textContent is property not a method (I mistakenly had method there :().. check updated answer, it should work :)
Brilliant :) . Thank you so much.
@ManuelZamith no worries, Its my hobby to help people :-)
1

In Angular4, to use highlight.js from component:

import {Component, ViewEncapsulation} from '@angular/core';
import * as hljs from 'highlight.js';

@Component({
    selector: 'cus-com',
    template: `
    <pre>
        <code [innerHtml]="html_content"></code>
    </pre>`,
    encapsulation: ViewEncapsulation.None,
    styles: [require('highlight.js/styles/github.css')]
})
export class CustomComponentsComponent {
    languages = ['html', 'typescript'];
    html_content = hljs.highlightAuto('<h1 class="big">Title</h1>', this.languages).value;  
}

Comments

-2

As mentioned in @PankajParkar comment, highlightBlock is getting fire before codeString binding gets evaluated.

Deferring running this function to the next tick (using setTimeout) will work as it will allow Angular to do change detection and DOM update.

The alternative solution is to use Pipe. Here is Pipe I use for code highlighting using Prism.js. You can easily update it to use Highlight.js:

@Pipe({ name: 'prism' })
export class PrismPipe implements PipeTransform {
  constructor(private sanitizer: DomSanitizer) {}
  transform(value, args) {
    if (!isString(value)) {
      throw new InvalidPipeArgumentException(PrismPipe, value);
    }
    let lang = args[0].toString().trim().toLowerCase();

    let grammar = Prism.languages[lang];
    // fallback to clike
    if (!grammar) grammar = Prism.languages.clike;
    return this.sanitizer.bypassSecurityTrustHtml(Prism.highlight(value, grammar));
  }
}

Then I use it in code like this:

<pre [innerHtml]="codeString.source | prism:codeString.lang"></pre>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.