Angular中表情符号和其他Unicode字符无法正确显示

5

我有一个使用Twitter消息的Web应用程序。

使用JSON提供数据来构建消息列表,但是像   这样的字符显示为带问号的菱形。该元素如下所示:

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

当我在Firefox和Chrome中查看JSON URL时,它们都正常显示。
头文件示例:
<!DOCTYPE html>
<html class="wf-opensans-n4-active wf-active" lang="en">
<head>
<meta charset="utf-8">

我在调试时发现一件事情:当所有消息都在一个对象数组中,而不是$scope的一部分时,我可以将它们添加到页面上并正确显示表情符号。
所以我认为这与Angular有关。我尝试将ng-bind-html更改为ng-bind,但那样做没有效果。我尝试删除ng-bind-html并在元素内使用{{unit.caption_text}},但仍会破坏Unicode字符。
目前,我需要能够使用linky筛选器来正确显示链接,因此ng-bind-html是必需的,但我不认为它是问题的原因。
它们在JavaScript中是否遇到了什么问题而导致编码错误?
有办法使它们正确显示吗?

更新

这样显示图标就像预期的那样,但“linky”不会对链接添加格式。

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

在此输入图片描述

这显示了损坏的字符。

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

更新2

最终完成了,按照Michael提供的Pull Request中详细说明的更改方法,解决了字符被搞乱的问题。

我还发现,如果在这些消息的字体栈中加入Symbola会有更好的一致性。你可以从George Douros的这个页面下载Symbola字体。我将它通过.ttf到.woff转换器进行了转换,获得了两个替代品,以获得稍微更好的浏览器支持。


请查看这个链接 https://github.com/madhur/angular-emoji-popup - Madhur Ahuja
2个回答

9
注意:此帖子在Chrome上显示不正常,因为它不支持表情符号字符。
看起来Angular的$ sanitize服务尝试将字符转换为其HTML实体等效项。但是,对于某些单个表情符号字符,它会将其拆分为2个。如http://plnkr.co/edit/fDDen3bnnrQUvx3JfKgw?p=preview所示。
$scope.sanitizedText = $sanitize('');

在模板中输出
{{sanitizedText}}

显示 &#55357;&#56904;。为什么?我不知道。

这意味着任何使用 $sanitize 的内容都将有效地破坏这些字符。这包括:

  • 未通过 $sce.trustAsHtml 处理的使用 ng-bind-html 显示的输出
  • 任何经过 linky 处理的内容,可以在 linky source 中看到,它调用了 $sanitize

因此,只要满足以下条件,就可以避免 HTML 经过 $sanitize 处理:

  • 将字符串通过 $sce.trustAsHtml 传递
  • 不要传递给 Angular 提供的 linky 过滤器,而是自己编写不会对输入进行 $sanitize 的过滤器。

以下是一个示例过滤器:

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>'));
  }
});

可以用作

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

你可以在http://plnkr.co/edit/sRJmt4YVO8udJInCd4Cy?p=preview中查看演示。
正如名称所示,这个unsafeLinky过滤器是不安全的。请确保信任原始文本的来源。
正如本答案开头所建议的那样,Chrome无法正确显示Emoji字符。要在Chrome中显示字符,您可能需要使用某种图像替换。无论如何,我认为这超出了这个特定问题的范围。 更新 有一个Angular PR可能会解决这个问题,一旦合并,上述解决方法就不再必要:https://github.com/angular/angular.js/pull/6911

显然$sanitize出了问题。这些引用解码为UTF-16的“代理对”机制保留的代码点;有人正在迭代UTF-16代码单元而不是实际代码点,从而破坏了BMP之外的所有内容。好样的库作者。 - Alex
Alex,我昨晚发现了这个并且它有效。你的答案是正确的!我还下载了一个字体来给表情符号提供一致的处理,并将其添加到了问题中。将在我的问题中引用它。 - daveyfaherty

1

我发现上面的正则表达式在一些链接(例如bit.yl短链接)上会出现问题,因此我改编了另一个我在某个地方找到的例子:

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);        
    };
});

使用方法:

<p ng-bind-html="tweet.text | parseUrl"></p>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接