5

I am trying to use multiple filters as below,

<p><span ng-bind-html="someVar | nl2br | linky"></span></p>

which renders nothing. However, when I change the order of filters as below

<p><span ng-bind-html="someVar | linky | nl2br"></span></p>

linky works, but nl2br fails to convert line breaks to br.

The following implementation can be used for nl2br:

.filter('nl2br', function($sce) {
  return function(input) {
    return $sce.trustAsHtml( input.replace(/\n/g, '<br>') );
  }
}
jsalonen
  • 29,593
  • 15
  • 91
  • 109
Nirav Gandhi
  • 1,945
  • 1
  • 23
  • 32
  • Reference code for nl2br filter http://pastebin.com/1TsdF0V6 – Nirav Gandhi Jan 22 '15 at 13:24
  • 1
    `$sce.trustAsHtml` doesn't return a string, `linky` expects one. – a better oliver Jan 22 '15 at 13:35
  • 1
    I'm opening up this question for a bounty since this is a common, yet unexpectedly complex problem that teaches you about stuff you didn't want to know about in the first place. I know its important to handle user-entered HTML as untrusted, but the complexity of using $sce with simple chain of two filters just blows my mind. – jsalonen Mar 24 '15 at 14:34

2 Answers2

10

So I was able to get it work with someVar | linky | nl2br . The problem was with linky filter. ngSanitize's linky filter changes \r and \n to &#10; and &#13; respectively. Given nl2br filter fails to catch those.

Thanks to this gist https://gist.github.com/kensnyder/49136af39457445e5982 , modified nl2br as follows

angular.module('myModule')
.filter('nl2br', ['$sanitize', function($sanitize) {
    var tag = (/xhtml/i).test(document.doctype) ? '<br />' : '<br>';
    return function(msg) {
        // ngSanitize's linky filter changes \r and \n to &#10; and &#13; respectively
        msg = (msg + '').replace(/(\r\n|\n\r|\r|\n|&#10;&#13;|&#13;&#10;|&#10;|&#13;)/g, tag + '$1');
        return $sanitize(msg);
    };
}]);

Working fiddle http://jsfiddle.net/fxpu89be/4/

However, it still doesn't solve the original problem of using it in reverse order i.e someVar | nl2br | linky

Nirav Gandhi
  • 1,945
  • 1
  • 23
  • 32
  • I like the way you clearly what is the problem with naive solution as well as provide simple filter that actually works. Bounty awarded. Thank you! – jsalonen Mar 31 '15 at 21:29
3

Building on zeroflagL's comment - keep it a normal sting until the end.

<p><span ng-bind-html="someVar | nl2br | linky | trustMe"></span></p>

Removing all trust - so that we return a normal string:

.filter('nl2br', function($sce) {
  return function(input) {
    return input.replace(/\n/g, '<br>');
  }
}

The last thing we want to do is add some trust:

.filter('trustMe', function($sce) {
  return function(input) {
    return $sce.trustAsHtml( input ) );
  }
}
Remento
  • 927
  • 4
  • 6
  • Thank you for your answer. There are two problems with this approach: firstly, as a minor note, you have several syntax errors. Secondly, it doesn't work; even if you run the output through `$sce.trustAsHtml` you get escaped HTML. See this fiddle for proof: http://jsfiddle.net/jsalonen/fxpu89be/2/ - if you proceed to address these problems I'm happy to give you the bounty. – jsalonen Mar 30 '15 at 11:30