Фильтр AngularJS, возвращающий html в виде строки

Я создал фильтр AngularJS для автоматического создания интерактивных ссылок из адресов, найденных в данных. Фильтр:

app.filter('parseUrl', function() {
    var  //URLs starting with http://, https://, or ftp://
        replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim,
        //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
        replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim,
        //Change email addresses to mailto:: links.
        replacePattern3 = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;

        return function(text, target, otherProp) {        
            angular.forEach(text.match(replacePattern1), function(url) {
                text = text.replace(replacePattern1, "<a href=\"$1\" target=\"_blank\">$1</a>");
            });
            angular.forEach(text.match(replacePattern2), function(url) {
                text = text.replace(replacePattern2, "$1<a href=\"http://$2\" target=\"_blank\">$2</a>");
            });
            angular.forEach(text.match(replacePattern3), function(url) {
                text = text.replace(replacePattern3, "<a href=\"mailto:$1\">$1</a>");
            });

            return text;        
        };
    });

И вот как я это называю (внутри абзаца):

<p><strong>Details:</strong> {{event.description | parseUrl}}</p>

И это правильно работает, чтобы заменить простые текстовые ссылки кодом ссылки. Однако он заменяет его ссылкой буквально как обычный текст. Например, www.google.com будет заменено на &lt;a href="http://www.google.com" target="_blank"&gt;http://google.com&lt;/a&gt;. Это явно не делает ссылку кликабельной, что было моей целью.

Я не уверен, почему это происходит. Любые идеи о том, как предотвратить/исправить это? Спасибо.


person Jakemmarsh    schedule 10.07.2013    source источник
comment
Вы можете взглянуть на фильтр ссылок, предоставляемый AngularJS (docs.angularjs.org/ api/ngSanitize.filter:linky) в модуле ngSanitize - этот фильтр может уже делать то, что вам нужно, и документация для него поддерживает предложения, сделанные в ответах относительно ng-bind-html / ng-bind -html-небезопасные директивы.   -  person CBerube    schedule 10.07.2013


Ответы (3)


Попробуйте использовать директиву ngBindHtmlUnsafe, чтобы HTML-код, созданный вашим фильтром, применялся как фактический innerHTML. содержимое элемента, например:

<span ng-bind-html-unsafe="event.description | parseUrl"></span>
person Jollymorphic    schedule 10.07.2013
comment
К сожалению, в новой версии AngularJS такой директивы нет. - person Stepan Suvorov; 10.02.2014
comment
Не рекомендуется использовать ng-bind-html-unsafe, так как это создает потенциальную угрозу безопасности. Я думаю, что это была одна из причин, почему он устарел. - person Petr Peller; 14.02.2014

Вам нужно использовать либо:

  • ngBindHtmlUnsafe
    Если вы на 100 % доверяете источнику контента
  • ngBindHtml
    Если вы не доверяете источнику контента

При выводе строки с помощью выражения будут экранированы все объекты html, которые вы ему передаете (символы, такие как ‹ > &)

person Clark Pan    schedule 10.07.2013

Я использовал этот фильтр некоторое время, как-то не замечая катастрофических результатов, которые он производил. Моя модифицированная версия здесь:

filter('parseUrl', function($sce) {
var  //URLs starting with http://, https://, or ftp://
    replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim,
    //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
    replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim,
    //Change email addresses to mailto:: links.
    replacePattern3 = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;

    return function(text, target, otherProp) {        
        text = (text + '').replace(/>/,"&gt;").replace(/</,"&lt;");
        text = (text + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1<br>$2');
        text = text.replace(replacePattern1, "<a href=\"$1\" target=\"_blank\">$1</a>");
        text = text.replace(replacePattern2, "<a href=\"http://$2\" target=\"_blank\">$2</a>");
        text = text.replace(replacePattern3, "<a href=\"mailto:$1\">$1</a>");
        return $sce.trustAsHtml(text);
    };
});

Обратите внимание, что он не использует angular.forEach! (?????) Когда это произошло, вывод стал бы взрывным. Предположительно, эта проблема связана с наличием нескольких совпадений!

person djvs    schedule 11.12.2014
comment
Вместо того, что было в моем основном вопросе, я фактически использовал его ответвление: gist.github.com /мини/6018010 - person Jakemmarsh; 12.12.2014