Syntax highlighting in Angular 2

I am creating a simple web application using Angular 2. I have two components. The first is a table with multiple rows of data. When a row is clicked, the data corresponding to the row is displayed in the second component. The data is XML loaded into a 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 background color
        this.selectedItemsXml = this.items[i].xml;
    }
    // ...other stuff again
}

//TableComponent 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 template
<div>
    <pre><code>{{xml}}</code></pre>
</div>

      

Everything worked fine until I wanted to add syntax highlighting for the xml. At first I wanted to use the ng2-prism plugin, but I had problems with it working correctly. After I saw problems in his git repo, I threw it away. Next, I tried to create a highlight.js directive based on this post: highlight.js doesn't work with Angular 2 . This method highlights the XML, but only the first row of the time is clicked. When another row is clicked, the new xml is not even displayed. I also tried using prism.js, but I have the same behavior. As soon as some XML string is first linked, rendered in a code element, and highlighted using highlight.js or prism.js, it remains.

I guess it has something to do with how DOM and data binding work in angular 2, because without using syntax highlighting, I bind and pass a string to a code element every time a string is selected. Using highlighting, you can concatenate a string, pass it to a code element, and then prefix it. This means that there is no simple line in the code element, but there are many style elements, which causes problems loading a new XML line when a new line is selected. I also tried to bind the "pre-pre-checked" string using Prism.highlight (text_to_prettify), but using this method renders the xml with all the span elements added by Prism.

Right now, I am racking my brains thinking about how to solve this problem. Please give me a push on how I can get this to work.

+4


source to share


3 answers


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



+5


source


I would create a component that uses prismjs.

Include the prismjs scripts including any of the required components folder.

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 - it is either out of date or not authorized correctly. The author prismjs

recommends @ ryancvanaugh / prismjs for type definitions.

Add the folder to your property list typeRoots

in tsconfig.json

so the typescript compiler can find it:

tsconfig.json (under compilerOptions)



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

      

Create a custom component prism

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 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>

      

+4


source


I am using PixelBits solution with slight modifications, first make sure you are not trying to connect the prism component in the App.component.ts file, you will get a "View changed after it checked" message and ngAfterViewInit () will not be called if you reload the page. So do some basic routing and page and try the prism component inside this new page. Then, if you want to use a plugin with line numbers, Prism.highlight won't work. You need to use Prism.highlightElement. Also with PixelBits solution you get white space before and after your code. Don't forget to change the url of the referenced path to match your project.

c

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 prism official site (plugin for line number is integrated inside prism.js).

0


source







All Articles