How to use SyntaxHighlighter on dynamically loaded code blocks with Angular 6?

571 Views Asked by At

I am trying to add SyntaxHighlighter to my angular blog project. My articles have <pre> formatted code snippets and is loaded from database. This article HTML is then displayed in ArticleComponent template. This ArticleComponent itself is loaded when a particular route is triggered. But when I browse to this route and an article is loaded, the <pre> formatted codes are not highlighted at all. However, if I refresh the page, highlighting works well. Not sure where I am going wrong.

index.html

<head>
    <meta charset="utf-8">
    <title>My Blog</title>
    <base href="/">

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">

    <link rel="stylesheet" href="./assets/css/foundation.css">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css" integrity="sha384-5sAR7xN1Nv6T6+dT2mhtzEpVJvfS3NScPQTrOxhwjIuvcA67KV2R5Jz6kr4abQsz" crossorigin="anonymous">
    <link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css" />
    <link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <app-root></app-root>

    <script type="text/javascript" src="./assets/js/jquery.js"></script>
    <script type="text/javascript" src="./assets/js/what-input.js"></script>
    <script type="text/javascript" src="./assets/js/foundation.js"></script>
    <script>
        $(document).foundation();
    </script>
    <script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
    <script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js" type="text/javascript" ></script>
    <script type="text/javascript">
        SyntaxHighlighter.defaults['toolbar'] = false;
        SyntaxHighlighter.all()
   </script>
</body>

article.component.html

<div class="cell">
    <span [innerHTML]="article"></span>
</div>

article.component.ts

export class ArticleComponent implements OnInit {

    public article = '';
    public title = '';

    constructor(private _route: ActivatedRoute, private _articleService: ArticleService) {
    }

    ngOnInit() {
        const articleId: string = this._route.snapshot.paramMap.get('id');

        const allArticles = this._articleService.getArticles();
        const selectedArticleMetaData = allArticles.find(x => x.id === parseInt(articleId, 10));
        this.title = selectedArticleMetaData.title;

        this._articleService
            .getArticleHtml(selectedArticleMetaData.id)
            .subscribe((article: string) => {
                this.article = article;
            });
    }

}

I checked the HTML DOM elements and I am able to verify that the article that is loaded without refresh has all its <pre> blocks as raw code. But when SyntaxHighlighter is actually working in case of refresh, these <pre> tags are all converted to div elements with proper style classes.

Looks like when when articles are loaded through binding, SyntaxHighlighter is unable to process the <pre> tags. Any idea how to fix this ? Also any idea why it works when I refresh this page ? In either case, the article is loaded through binding.

2

There are 2 best solutions below

0
AudioBubble On BEST ANSWER

Solved it. Answering it here as it might be helpful to some one else.

SyntaxHighlighter.all() method registers a Highlight method to windows.load event. Hence this method is only triggered on page load and never later. This solves the mystery of why highlighting worked during page refresh and not when angular routing is triggered.

The fix is to trigger a window.load event after article is loaded in the DOM. There are better ways to do this, but here is the most straight forward way to do this. Update article.component.ts to include the code to trigger window.load event.

this._articleService
    .getArticleHtml(selectedArticleMetaData.id)
    .subscribe((article: string) => {
        this.article = article;
        this.loading = false;

        // trigger window.load event after 500 ms of binding the article property
        setTimeout(() => {
            window.dispatchEvent(new Event('load'));
        }, 500);
    });
0
Nabil Shahid On

The SyntaxHighlighter.all() method which highlights all html with <pre> tag, is called only once i.e. when the page is loaded because you included it in the script tag of index.html. When this method is called, it will highlight all available <pre> on the page.

When you go to a route, the SyntaxHighlighter.all() function needs to be called which will highlight the new generated tags or text inside that tag.

When you refresh the page, <pre> tags are available on the page html when SyntaxHighlighter.all() is called which is the reason, it is highlighting correctly.

So for articles that are loaded through binding, call SyntaxHighlighter.all() in logic once to highlight your <pre> tags.

You need to choose the right event, where it should be called in your component and also, you need the define a variable at the top of your component below your import statements so that tslint should let you use that variable in your component i.e. declare var SyntaxHighlighter