質問

I have a web app that uses messages from Twitter, for example.

A feed of JSON is used to build a list of messages, but characters such as 🙏👍👊 are displaying as question marks in a diamond. The element looks like this:

<div class="inner-message" ng-bind-html="unit.caption_text | linky:'_blank'"></div>           

When I view the JSON url in Firefox and Chrome, these display fine.

Sample of head:

<!DOCTYPE html>
<html class="wf-opensans-n4-active wf-active" lang="en">
<head>
<meta charset="utf-8">

One thing I've found while debugging: when the messages are all in an array of objects, but not part of $scope, I can add them to the page and the emoji show correctly.

So I think something happens in Angular to do this. I've tried changing ng-bind-html to ng-bind, but that doesn't do it. I've tried removing ng-bind-html and using {{unit.caption_text}} inside the element, but it still breaks the unicode characters.

At the moment, I need to be able to use the linky filter, to display links correctly, so ng-bind-html is necessary, but I don't believe it's the problem.

Is something happening to them in javascript to break the encoding?

Is there a way to make them display correctly?


Update

This shows icons as desired, but "linky" doesn't add formatting to links.

<div class="inner-message">{{unit.text}}</div>

enter image description here

This shows broken characters

<div class="inner-message" ng-bind-html="unit.text | linky:'_blank'"></div>

enter image description here


Update 2

Got there in the end, making the changes as detailed in the Pull Request, which Michael linked, to stop the characters getting messed up.

I also found it helped give consistency if I added Symbola to the font stack for these messages. You can download Symbola from this page by George Douros . I ran it through a .ttf to .woff converter to get slightly better browser support, offering two alternatives.

役に立ちましたか?

解決

Note: This post does not display as intended on Chrome, as it doesn't support Emoji characters

It looks like Angular's $sanitize service attempts to convert the characters to their HTML-entity equivalent. However, for certain single emoji characters, it splits it to 2. As can be seen at http://plnkr.co/edit/fDDen3bnnrQUvx3JfKgw?p=preview,

$scope.sanitizedText = $sanitize('🙈');

output in the template as

{{sanitizedText}}

shows &#55357;&#56904;. Why? I don't know.

This means that anything using $sanitize will effectively break such characters. This includes

  • Output shown using ng-bind-html that hasn't gone through $sce.trustAsHtml
  • Anything passed through linky, as can be seen in the linky source, it calls $sanitize.

So, you can avoid HTML going through $sanitize as long as

  • You pass the string through $sce.trustAsHtml
  • You don't pass it through the Angular provided linky filter, but roll your own that doesn't $sanitize the input.

An example filter is below:

app.filter('unsafeLinky', function($sce) {
  // Regex from https://github.com/angular/angular.js/blob/master/src/ngSanitize/filter/linky.js#L5
  var urlRegex = /(((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>])/gi;
  return function(input, target) {
    var targetHTML = target ? ' target="' + target + '"' : '';
    return $sce.trustAsHtml(input.replace(urlRegex,'<a href="$1"' + targetHTML + '>$1</a>'));
  }
});

Which can be used as

<p ng-bind-html="text | unsafeLinky:'_blank'"></p>

You can see a demo of this at http://plnkr.co/edit/sRJmt4YVO8udJInCd4Cy?p=preview

As suggested by the name, this unsafeLinky filter is unsafe. Be sure to trust where the original text comes from.

As suggested at the start of this answer, Chrome doesn't display Emoji characters properly. To get the characters showing in Chrome, you might have to use some sort of image-replacement. In any case, I suspect that is beyond the scope of this particular question.

Update

There is PR for Angular that might fix this, making the above work-around unnecessary once it's merged in: https://github.com/angular/angular.js/pull/6911

他のヒント

I found that the regex above was breaking on some urls such as bit.yl shortlinks so i adapted another example i found somewhere:

twitterApp.filter('parseUrl', function($sce) {
    var urlPattern = /(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&amp;:\/~+#-]*[\w@?^=%&amp;\/~+#-])?/gi;
    return function(text) {        
        angular.forEach(text.match(urlPattern), function(url) {
            text = text.replace(url, "<a target=\"_blank\" href="+ url + ">" + url +"</a>");
        });
        return $sce.trustAsHtml(text);        
    };
});

Usage:

<p ng-bind-html="tweet.text | parseUrl"></p>
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top