88

I'm trying to convert newline characters (\n) to html br's.
As per this discussion in the Google Group, here's what I've got:

myApp.filter('newlines', function () {
    return function(text) {
        return text.replace(/\n/g, '<br/>');
    }
});

The discussion there also advises to use the following in the view:

{{ dataFromModel | newline | html }}

This seems to be using the old html filter, whereas now we're supposed to use the ng-bind-html attribute.


Regardless, this poses a problem: I don't want any HTML from the original string (dataFromModel) to be rendered as HTML; only the br's.

For example, given the following string:

While 7 > 5
I still don't want html & stuff in here...

I'd want it to output:

While 7 &gt; 5<br>I still don't want html &amp; stuff in here...

Is there any way to accomplish this?

MegaHit
  • 2,614
  • 4
  • 24
  • 27

7 Answers7

281

Maybe you can achieve this only with html, a <preformated text> way ? It will avoid from using filters or do any kind of processing.

All you have to do is display the text within an element that has this CSS:

<p style="white-space: pre;">{{ MyMultiLineText}}</p>

This will parse and display \n as new lines. Works great for me.

Here, a jsFiddle example.

Milche Patern
  • 19,632
  • 6
  • 35
  • 52
Devin Spikowski
  • 2,946
  • 2
  • 12
  • 13
  • 79
    pre-line, was a better option for me. – Pepijn Dec 18 '13 at 19:57
  • 7
    +1, this is by far the simplest solution and seems to be suitable for many needs. Indeed, `pre-line` is probably better in general, since long rows will be wrapped (as they would with any `
    ` based solutions).
    – tuomassalo Dec 19 '13 at 09:03
  • +1 WOW. This is an incredible solution that takes into account that we'd rather not rely on our own escaping abilities. We need to be able to display line breaks without compromising XSS protections. – C Fairweather Jan 30 '14 at 06:14
  • 13
    style="white-space: pre-line;" is better option to use inside
    , on my opinion
    – Dmitri Algazin Feb 28 '14 at 16:08
  • 7
    `pre-wrap` seems to be what most people want (not pre-line): "Whitespace is preserved by the browser. Text will wrap when necessary, and on line breaks" from [w3schools](http://www.w3schools.com/cssref/pr_text_white-space.asp) – qwertzguy Oct 09 '14 at 23:53
  • One thing worth noting is that adding `word-wrap: break-word;` will prevent the pre from breaking word wrapping (`pre-line` didn't work for me on all browsers). – Michael Dec 04 '14 at 15:18
  • 2
    I found I needed to use ng-bind="MyMultiLineText" on the

    to stop Chrome adding extra lines infront of my text

    – Scott Warren Aug 25 '15 at 23:39
  • Nice thought about the pre-line. Thanks ! – MadJlzz Apr 05 '16 at 12:47
  • @ScottWarren, thanks you so much for that comment. I've been trying to figure out where the leading and ending white space was coming from. It isn't intuitive using pre-wrap for the first time. However, there is another way on the same idea, don't have any space between the tags the angular expression is between, for example,
    {{vm.expression}}
    without any spaces and character return between.
    – Adam S Sep 18 '16 at 22:59
  • When you select the text with the mouse and copy it to the text box the new line won't be preserved. – sivaram636 Oct 25 '16 at 05:39
33

Instead of messing with new directives, I decided to just use 2 filters:

App.filter('newlines', function () {
    return function(text) {
        return text.replace(/\n/g, '<br/>');
    }
})
.filter('noHTML', function () {
    return function(text) {
        return text
                .replace(/&/g, '&amp;')
                .replace(/>/g, '&gt;')
                .replace(/</g, '&lt;');
    }
});

Then, in my view, I pipe one into the other:

<span ng-bind-html-unsafe="dataFromModel | noHTML | newlines"></span>
MegaHit
  • 2,614
  • 4
  • 24
  • 27
  • Your regex for new lines wont' work. You need: `text.replace(/\\n/g, '
    ')` or even better `text.replace(/(\\r)?\\n/g, '
    ')`
    – Liglo App Jan 17 '14 at 15:06
  • 2
    @BarthZalewski - You only need `\\` when compiling a regex from a string. When using a regex literal you don't have to escape slashes. – MegaHit Jan 31 '14 at 01:28
  • 2
    This code no longer works since ng-bind-html-unsafe is deprecated. – Abhi Feb 19 '14 at 14:12
  • 1
    You can now skip the noHtml filter if you want and just add the newLines filter to ng-bind-html. ngSanitize will take care of the rest. – Dave Merwin Aug 01 '14 at 20:20
26

A simpler way to do this is to make a filter that splits the text at each \n into a list, and then to use `ng-repeat.

The filter:

App.filter('newlines', function() {
  return function(text) {
    return text.split(/\n/g);
  };
});

and in the html:

<span ng-repeat="line in (text | newlines) track by $index">
    <p> {{line}}</p>
    <br>
</span>
JJW5432
  • 697
  • 8
  • 15
11

If you do not want to destroy the layout with endless strings, use pre-line:

<p style="white-space: pre-line;">{{ MyMultiLineText}}</p>
Sebastian Viereck
  • 5,455
  • 53
  • 53
6

I'm not aware if Angular has a service to strip html, but it seems you need to remove html before passing your newlines custom filter. The way I would do it is through a custom no-html directive, which would be passed a scope property and the name of a filter to apply after removing the html

<div no-html="data" post-filter="newlines"></div>

Here's the implementation

app.directive('noHtml', function($filter){
  return function(scope, element, attrs){
    var html = scope[attrs.noHtml];
    var text = angular.element("<div>").html(html).text();

    // post filter
    var filter = attrs.postFilter;
    var result = $filter(filter)(text);

    // apending html
    element.html(result);
  };
});

The important bit is the text variable. Here I create an intermediate DOM element and append it the HTML using the html method and then retrieve only the text with the text method. Both methods are provided by Angular's lite version of jQuery.

The following part is applying the newline filter, which is done using the $filter service.

Check the plunker here: http://plnkr.co/edit/SEtHH5eUgFEtC92Czq7T?p=preview

jaime
  • 41,961
  • 10
  • 82
  • 52
2

An update to the filter with ng-bind-html currently would be:

myApp.filter('newlines', function () {
  return function(text) {
    return text.replace(/(&#13;)?&#10;/g, '<br/>');
  }
});

and the noHTML filter is no longer required.

white-space solution is having low browser support: http://caniuse.com/#search=tab-size

Alex Mounir
  • 1,243
  • 1
  • 14
  • 20
0

Bit late to the party on this but I would suggest a small improvement to check for undefined / null strings.

Something like:

.filter('newlines', function () {
    return function(text) {
        return (text) ? text.replace(/(&#13;)?&#10;/g, '<br/>') : text;
    };
})

Or (bit tighter)

.filter('newlines', function () {
    return function(text) {
        return (text instanceof String || typeof text === "string") ? text.replace(/(&#13;)?&#10;/g, '<br/>') : text;
    };
})
Ben Edge
  • 314
  • 1
  • 4
  • 5