50

I'm trying to create an Angular2 custom pipe that outputs raw html. I want it to simply convert newlines in the input into HTML line breaks. How do I output raw HTML from an angular2 pipe?

The following is my current pipe definition, but it escapes the HTML which I don't want:

import {Pipe, PipeTransform} from '@angular/core';
/*
 * Converts newlines into html breaks
*/
@Pipe({ name: 'newline' })
export class NewlinePipe implements PipeTransform {
    transform(value: string, args: string[]): any {
        return value.replace(/(?:\r\n|\r|\n)/g, '<br />');
    }
}

EDIT: Just a quick note since this question seems to get a number of views and activity that I'm leaving my accepted answer as is, even though for my specific example use-case css probably would have been a better option and was in fact what I changed to. But my actual question was "how do I output raw html from an angular 2 pipe", not "what's the best way to render line breaks" which is what the accepted answer shows.

Beware though, as mentioned in comments below, if you do use the method in the accepted answer you need to ensure that you're not rendering unchecked user input as this could open you up to XSS vulnerabilities.

mutex
  • 7,536
  • 8
  • 45
  • 66

9 Answers9

123

You could also use pure CSS

<span class="line-breaker">
{{text}}
</span>

.line-breaker {
  white-space: pre-line;
}
Toolkit
  • 10,779
  • 8
  • 59
  • 68
62

The answer (I found just after posting the question) is not to change anything in the pipe definition, but instead to bind to [innerHtml] when using the pipe.

So replace this:

<div class="panel-body">
    {{mycontent | newline}}
</div>

with this:

<div class="panel-body" [innerHtml]="myContent | newline">
</div>

It would be nice if there were way to do this in the pipe definition though...

EDIT: Just a quick note since this answer seems to attract a number of downvotes that I'm leaving it as is, even though for my specific example use-case css probably would have been a better option and was in fact what I changed to use.

BUT my actual question was "how do I output raw html from an angular 2 pipe", not "what's the best way to render line breaks." This answer shows how to do this.

BEWARE though, as mentioned in comments below, if you do use the method in this answer you need to ensure that you're not rendering unchecked user input as this could open you up to XSS vulnerabilities.

mutex
  • 7,536
  • 8
  • 45
  • 66
  • 6
    Please do not use the accepted solution as it introduces an XSS vulnerability. Any HTML content in myContent will not be escaped and will be returned to the user as is. – Bryan Burman Jan 10 '17 at 19:34
  • 7
    Only if you're using it to render user input - in which case yes, you will need to ensure the input is "safe" using some other mechanism; e.g. server side. – mutex Jan 11 '17 at 20:49
  • 12
    Upvoted to compensate downvotes for XSS vulnerability. The vulnerability only affects HTML from user input. There are server side mechanisms to control the user input that this answer doesn't override. – JoeCool May 02 '17 at 20:42
  • Is this really the only way to do this. I'm fine with it for my needs (adding a linebreak in a series of headers - where the position needs to be manually tweaked). Good to know the pipe isn't the problem but the usage of it. – Simon_Weaver Jun 05 '18 at 21:23
  • 3
    There is no problem with the accepted solution. As it said in the Angular documentation, it looks like there are no worries about XSS because Angular automatically recognizes the unsafe values and sanitizes them. https://angular.io/guide/security#sanitization-example – Mr. Mars Jun 19 '19 at 00:42
23

As a variation of the above CSS answer by Toolkit, if you want whitespace to be counted rather than collapsed, you can use the following code instead.

HTML

<span class="line-breaker">
  {{text}}
</span>

CSS

.line-breaker {
  white-space: pre-wrap;
}

What pre-wrap does (in addition to showing line breaks and wrapping etc like pre-line) is show every space as a space, rather than collapsing multiple spaces into one.

normal

Sequences of whitespace are collapsed. Newline characters in the source are handled the same as other whitespace. Lines are broken as necessary to fill line boxes.

nowrap

Collapses whitespace as for normal, but suppresses line breaks (text wrapping) within the source.

pre

Sequences of whitespace are preserved. Lines are only broken at newline characters in the source and at <br> elements.

pre-wrap

Sequences of whitespace are preserved. Lines are broken at newline characters, at <br>, and as necessary to fill line boxes.

pre-line

Sequences of whitespace are collapsed. Lines are broken at newline characters, at <br>, and as necessary to fill line boxes.

Source: https://developer.mozilla.org/en/docs/Web/CSS/white-space

redfox05
  • 3,354
  • 1
  • 34
  • 39
5

If you are binding Typescript variable in HTML and it contains '\n' then you need to replace them < br/> otherwise keep < br/> from beginning.

However String interpolation will not break the line you need to use property binding with innerHtml like:

<span [innerHtml]="stringVariableName"></span>
Rohan Shenoy
  • 805
  • 10
  • 23
3
<p *ngFor="let myContent of myContents.split('\n')">{{ myContent }} </p>

This thing will do the same....

Tibin Thomas
  • 742
  • 5
  • 12
1

Here's my take on it, using a simple pipe

make a file called nl2pbr.pipe (which stands for: new line to p / br) and put it inside pipes folder, and this is its content:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'nl2pbr'
})
export class Nl2pbrPipe implements PipeTransform {

  transform(value: any, args?: any): any {
    // return value.replace(/\n/g, '<br />');
    value = value.replace(/(?:\r\n\r\n|\r\r|\n\n)/g, '</p><p>');
    return '<p>' + value.replace(/(?:\r\n|\r|\n)/g, '<br>') + '</p>';
  }

/****************************
 * example useage
 * <div [innerHTML]="'testi\n\nng \n t/n/nest\n\b\ning' | translate | nl2pbr"></div>
 ****************************/

}

Make sure you add the pipe to app.module:

import { Nl2pbrPipe } from './pipes/`nl2pbr.pipe`';

and Nl2pbrPipe to the declarations: []

Usage example:

<div [innerHTML]="'hello\n\n this is a new paragraph\n and a new line' | translate | nl2pbr"></div>

will output:

<p>hello</p>
<p>this is a new paragraph <br/> and a new line</p>
Community
  • 1
  • 1
Elron
  • 1,235
  • 1
  • 13
  • 26
1

use angular directive

import { Directive, ElementRef } from '@angular/core';

@Directive({
    selector: '[shareLineBreaker]',
})
export class LineBreakerDirective {
    constructor(el: ElementRef) {
        el.nativeElement.style['white-space'] = 'pre-line'
    }
}

then

<td shareLineBreaker>
                    {{data.users |  arraymap:'\n':'n':'l' }}
                </td>
吴祥平
  • 11
  • 1
0

What about:

<pre>{{text}}</pre>

Or for html:

<pre [innerHtml]="text"></pre>

I often use the HTML pre element that represents preformatted text.

Another trick I often use to format Json is:

<pre>{{jsonString | json}}</pre>
Michael Karén
  • 1,105
  • 9
  • 13
0
value.replace(/(?:\\r\\n|\\r|\\n)/g, '<br />');

works for me

Lean Pilar
  • 327
  • 7
  • 13