2

I have situation where the data in my angular controller is populated from a CMS. Some of this data includes urls that may contain accented characters as html entities (non-english language, é á etc.) This url data then bound to the href attribute of an anchor tag. However I cannot seem to get the data to bind without (I think) angular escaping the html. If I use ng-init to set the same url the binding works fine. This is not an option for my project though. The example (& JSFiddle) attached illustrates where the controller renders invalid links (I assume as a result of escaping) when binding data from the model but the value from ng-init renders correctly.

<body ng-app="myApp" ng-controller="myCtrl">
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
  <div>
    <h1>Example from model json</h1>
    <p>Link (href): <a href="{{item1.url}}">{{item1.url}}</a>
    </p>
    <p>Link (ng-href): <a ng-href="{{item1.url}}">{{item1.url}}</a>
    </p>
  </div>

  <div>
    <h1>Example from ng-init</h1>
    <div ng-init="item2 = {'url':'http://www.example.com/file-&#225;&#250;&#243;.pdf'}"></div>
    <p>Link (href): <a href="{{item2.url}}">{{item2.url}}</a>
    </p>
    <p>Link (ng-href): <a ng-href="{{item2.url}}">{{item2.url}}</a>
    </p>
  </div>

  <script>
    var app = angular.module('myApp', []);
    app.controller('myCtrl', ['$scope',
      function($scope) {
        $scope.item1 = {
          'url': 'http://www.example.com/file-&#225;&#250;&#243;.pdf'
        };
      }
    ]);
  </script>
</body>
Killian
  • 414
  • 2
  • 10

1 Answers1

1

It sounds like what you need is a way to decode HTML entities in pure JavaScript. Using the code provided in this previous question related to parsing "&\amp;" as a jumping off point, I made a new fiddle here that shows you how to decode the HTML entities without using ng-init. I also took the liberty of removing the extra code since you aren't doing any encoding (it would be more trouble than it's worth to remove it from the enclosing object since the original coder uses the object to do more than just store the code). Also, here's the code in example format:

<body ng-app="myApp" ng-controller="myCtrl">
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
  <div>
    <h1>Example from model json</h1>
    <p>Link (href): <a href="{{htmlEnDeCode.htmlDecode(item1.url)}}">{{htmlEnDeCode.htmlDecode(item1.url)}}</a>
    </p>
    <p>Link (ng-href): <a ng-href="{{htmlEnDeCode.htmlDecode(item1.url)}}">{{htmlEnDeCode.htmlDecode(item1.url)}}</a>
    </p>
  </div>

  <div>
    <h1>Example from ng-init</h1>
    <div ng-init="item2 = {'url':'http://www.example.com/file-&#225;&#250;&#243;.pdf'}"></div>
    <p>Link (href): <a href="{{item2.url}}">{{item2.url}}</a>
    </p>
    <p>Link (ng-href): <a ng-href="{{item2.url}}">{{item2.url}}</a>
    </p>
  </div>

  <script>
    var app = angular.module('myApp', []);
    app.controller('myCtrl', ['$scope', function($scope) {
      $scope.item1 = {
        'url': 'http://www.example.com/file-&#225;&#250;&#243;.pdf'
      };
      // Credit to WaiKit Kung for SO answer at https://stackoverflow.com/a/20880789/4504895
      $scope.htmlEnDeCode = (function() {
        var charToEntityRegex,
          entityToCharRegex,
          charToEntity,
          entityToChar;

        function resetCharacterEntities() {
          charToEntity = {};
          entityToChar = {};
          // add the default set
          addCharacterEntities({
            '&amp;': '&',
            '&gt;': '>',
            '&lt;': '<',
            '&quot;': '"',
            '&#39;': "'"
          });
        }

        function addCharacterEntities(newEntities) {
          var charKeys = [],
            entityKeys = [],
            key, echar;
          for (key in newEntities) {
            echar = newEntities[key];
            entityToChar[key] = echar;
            charToEntity[echar] = key;
            charKeys.push(echar);
            entityKeys.push(key);
          }
          charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
          entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
        }

        function htmlDecode(value) {
          var htmlDecodeReplaceFn = function(match, capture) {
            return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
          };

          return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
        }

        resetCharacterEntities();

        return {
          htmlDecode: htmlDecode
        };
      })();
    }]);

  </script>
</body>
Community
  • 1
  • 1
Peter G
  • 2,773
  • 3
  • 25
  • 35
  • Thanks for that. It certainly has the desired outcome. I'll hold off on accepting it as the answer for now though in case anybody has a more angular-centric approach. – Killian Jan 20 '17 at 10:31