Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

243
Views
Syntax highlighting in Angular 2

I'm creating simple web app using angular 2. I have two components there. First is basically table with some data rows. When click on row is performed, data corresponding to row are displayed in the second component. Data are XML, loaded to code element. It looks something like

@Component
export class TableComponent {
    items: Data[];
    selectedItemsXml: string;
    // ...other stuff

    //when row is clicked
    toggleSelectedRow(rowIndex: number) {
        // ...other stuff related to change row's background color
        this.selectedItemsXml = this.items[i].xml;
    }
    // ...other stuff again
}

//TableComponent's template
<div>
    <table>
        ...
        ...*ngFor="let item of items; let i = index;"...
        <tr (click)="toggleSelectedRow(i)">
            <td>{{item.id}}</td>
            <td>{{item.time}}</td>
        </tr>
        ...
    </table>
</div>
<xml-detail [xml]="selectedItemsXml"></xml-detail>

@Component
export class XmlDetailComponent {
    @Input() xml: string;
}

//XmlDetailComponent's template
<div>
    <pre><code>{{xml}}</code></pre>
</div>

Everything worked fine until I wanted to add syntax highlighting for xml. First I wanted to use plugin ng2-prism, but I had problems to make it work correctly. After I saw issues in its git repo, I threw it away. What I tried next was to create directive using highlight.js based on this post: highlight.js does not work with Angular 2. Xml is highlighted using this method, but only the first time row is clicked. When another row is clicked, new xml is not even displayed. I also tried to use prism.js but I'm getting the same behavior. Once some xml string is first time binded, displayed in code element and highlighted using either highlight.js or prism.js, it remains.

My guess is it is related with how DOM and data binding works in angular 2, because without using syntax highlighting, I'm binding and passing string to code element every time row is selected. Using highlighting causes to bind string, pass it to code element and then pretiffy it. That means there is no simple string inside code element, but a lot of styled span elements, what causes problems to load new xml string when new row is selected. I also tried to bind "pre-prettified" string using Prism.highlight(text_to_prettify), but using this method causes to display xml with all of the span elements added by Prism.

Right now, I'm scratching my head thinking about how to solve this problem. Please give me some push how could I make it work.

about 4 years ago · Santiago Trujillo
3 answers
Answer question

0

I would create a component that leverages prismjs.

Include the prismjs scripts including any from the components folder that you need.

Index.html

<link href="node_modules/prismjs/themes/prism.css" rel="stylesheet" />
<script src="node_modules/prismjs/prism.js"></script>
<script src="node_modules/prismjs/components/prism-core.js"></script>
<script src="node_modules/prismjs/components/prism-clike.js"></script>
<script src="node_modules/prismjs/components/prism-csharp.js"></script>

Install the latest type definition files for Prism (this will give you type-safety):

npm install @ryancavanaugh/prismjs

Note: don't install @types/prismjs - its either out-of-date or not authored correctly. The author of prismjs recommends @ryancvanaugh/prismjs for the type definitions.

Add the folder to your typeRoots list property in tsconfig.json so that the typescript compiler can find it:

tsconfig.json (under compilerOptions)

"typeRoots": [
  "node_modules/@types",
  "node_modules/@ryancavanaugh"
]

Create a custom prism component that calls Prism.highlight:

prism.component.ts

/// <reference path="../node_modules/@ryancavanaugh/prismjs/prism.d.ts" />
import { 
    Component, 
    AfterViewInit, 
    Input, 
    ElementRef, 
    ViewChild 
} from '@angular/core';

@Component({
    moduleId: module.id,
    selector: 'prism',
    template: `
        <div hidden="true" #rawContent>
            <ng-content></ng-content>
        </div>
        <pre>
            <code [innerHTML]="content">
            </code>
        </pre>

    `
})
export class PrismComponent implements AfterViewInit {
    @Input() language: string;
    @ViewChild("rawContent") rawContent: ElementRef;
    content: string;

    constructor(private elementRef:ElementRef) {
     }

    ngAfterViewInit() {
        this.content = Prism.highlight(this.rawContent.nativeElement.innerHTML, Prism.languages[this.language]);
     }
}

Declare the Prism component in app.module:

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule  } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { PrismComponent } from './prism.component';

@NgModule({
    imports: [
        BrowserModule,
        HttpModule        
        ],
    declarations: [AppComponent, PrismComponent],
    providers: [/* TODO: Providers go here */],
    bootstrap: [AppComponent],
})
export class AppModule { }

Use it like this:

<prism language="csharp">
    var x = 3;
    class T
    {{ '{' }}
    {{ '}' }}
</prism>
about 4 years ago · Santiago Trujillo Report

0

You can try this simple angular-prism component I wrote for Angular 2/4 since ng2-prism is no longer maintained and does not work for latest angular releases.

about 4 years ago · Santiago Trujillo Report

0

I use PixelBits solution, with few changes, first make sure you don't try the prism component in the App.component.ts file, you would get a "View changed after it has been checked" error and ngAfterViewInit() would not be called if you reload the page. So make a basic routing, and a page and trys your prism component inside that new page. Next if you want to use line-number plugin, Prism.highlight won't work. You need to use Prism.highlightElement. Also with PixelBits solution you get empty space before and after your code. Don't forget to change reference path url so it match your project.

ts:

export class PrismComponent implements OnInit,AfterViewInit {
    @Input() language: string;
    @ViewChild("rawContent") rawContent: ElementRef;
    @ViewChild("codeRef") codeRef: ElementRef;
    content: string;
    myClass:string;

    constructor(
        private elementRef:ElementRef,
    ) {
    }

    ngOnInit(){
        this.myClass = "language-" + this.language;
    }

    ngAfterViewInit() {
        // trim because ng-content add space
        this.content = this.rawContent.nativeElement.innerHTML.trim();
        // make line number plugin work.
        Prism.highlightElement(this.codeRef.nativeElement,true);
    }
}

and html:

<pre class="line-numbers"><code #codeRef [innerHTML]="content" [class]="myClass"></code></pre>
<div style="display: none;" hidden="true" #rawContent>
  <ng-content></ng-content>
</div>

Also i don't use npm to get prism.js, but i took it from the official prism website (plugin for line-number is integrated inside prism.js).

about 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!