0

I am having trouble putting a tag into my AngularJS web app. It loads data from a JSON file just fine. It also recognizes my HTML tags such as <em> and <br />. I achieved this through the $sce trustAsHtml. But it does not work for <input> tags. I have tried for two days but I could not find a solution that works. Help and explanations are appreciated!

index.html

<div ng-bind-html="myName.name"></div> <!-- The Test from the data.json file shows up in italic but the input does not show up in the DOM -->

app.js

var myApp = angular.module('myApp', [
    'ngRoute',
    'ngSanitize',
    'ngAnimate',
    'QuizController'
]);

myApp.filter('trustAsHtml', [
    '$sce',
    function($sce) {
        return function(value) {
            return $sce.trustAsHtml(value);
        }
    }
]);

data.json

"name" : "<em>Test</em> <input type='text' name='Hello'>"
user5854648
  • 1,029
  • 2
  • 11
  • 36

2 Answers2

2

You have defined a filter (trustAsHtml), which you don't apply to your value. Therefore the contents of ng-bind-html is not run through the filter and is not, actually, trusted.

As pointed out by georgeawg in the comments below, a number of safe tags are, in fact, allowed through ng-bind-html without the need to be run through $sce.trustAsHtml() and <em> is one of those tags.

But <input> is not.

Therefore, you need to apply your filter to allow unsafe tags through ng-bind-html, by changing the markup to:

<div class="txt" ng-bind-html="myName.name | trustAsHtml"></div>

It should work, though I haven't actually tested it in AngularJS environment. To do it, I'd first need to know what version you're using.

See it working here:

(function(){
  // Declare App
  var app = angular.module('testApp',[]);
  
  
  app.controller('testCtrl', ['$scope','$timeout', function($scope, $timeout){
    $scope.waiting = true;
    $timeout(() => {
      $scope.waiting = false;
      $scope.name = "<em>Test</em> <input type='text' name='Hello'>";
    }, 1000);
  }]).filter('trustAsHtml', [
    '$sce',
    function($sce) {
        return function(value) {
          return $sce.trustAsHtml(value);
        }
    }
  ]);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.7/angular.min.js"></script>

<div ng-app="testApp">
 <div ng-controller="testCtrl">
  <div ng-bind-html="name | trustAsHtml"></div>
  <div ng-show="waiting">let's wait a second...</div>
  </div>
</div>
tao
  • 82,996
  • 16
  • 114
  • 150
  • The txt class is not adding the italic, it does indeed work as I am looking at the italic text and can change it to use
    and other tags. I did try the actual filter at one point but I got this error message in the console: Error "
    " angular.min.js:125
    – user5854648 Apr 06 '18 at 21:54
  • To answer your question I am using AngularJS v1.6.7 – user5854648 Apr 06 '18 at 21:58
  • 1
    If the text is italic is not proof `` passes `ng-bind-html`. The only proof it does is if you inspect the source and you see the `` tag inside `
    ` in the live page. Even so, I'm telling you, you're not running the contents of `myName.name` through the filter you defined. Ok, I'll try to recreate using 1.6.7. I haven't worked with 1.6 much.
    – tao Apr 06 '18 at 21:59
  • I believe you, I am unsure why it works. I checked the DOM tree and I can confirm Test is wrapped in tags – user5854648 Apr 06 '18 at 22:02
  • In that case `ng-bind-html` allows harmless tags in 1.6 (which is nice, but totally unexpected - therefore not used by many users coming from older versions). It didn't use to work this way up to 1.5 - any HTML had to be run through `$sce`). I'll fiddle your example and see if I run into any probs. – tao Apr 06 '18 at 22:04
  • @user5854648, I added a basic example, using 1.6. As you can see, it works. If it doesn't in your case, your problem is elsewhere. You need to reproduce it here if you want it debugged. Since I don't have a json source, I just added the contents later, using a timeout. Do note that if you newly added HTML has anything that needs to be compiled by angular, it will only work once you run it through `$compile`. – tao Apr 06 '18 at 22:46
  • This was really helpful. So I had the .filter in the app.js so based on your example I removed it and tacked it to the end of my MainController instead and this caused the input to work. I suppose in retrospect that makes more sense, but I had created a app before with it in the app.js but never used the tag and I simply didn't know any better. Thanks for your help! – user5854648 Apr 06 '18 at 22:57
  • The `ng-bind-html` directive allows the `` tag because it is on the while list of trusted elements. See the [source code for ngSanitize.js L346](https://github.com/angular/angular.js/blob/master/src/ngSanitize/sanitize.js#L346). – georgeawg Apr 07 '18 at 03:15
  • @george, thanks. Already figured that out. What I didn't, yet, was in what version the list was implemented. – tao Apr 07 '18 at 14:37
  • That whitelist was in `ngSanitize.js` when it was initially committed Oct 18, 2010 with [committ 4fdab37](https://github.com/angular/angular.js/commit/4fdab3765919e9fffc6d2f84e74754b1012997be#diff-953786501fb82b0101b7d15a1b3873cb). That means it has worked that way since V1.0.rc2 – georgeawg Apr 07 '18 at 19:54
  • @georgeawg I remember needing `$sce.trustAsHtml()` for `` elements a couple of years back, on `v1.2`. Of course, I acknowledge human memory is not, technically, a reliable source of information. :) – tao Apr 07 '18 at 19:59
1

From you example it's unclear how do you use trustAsHtml filter.

Anyway, seems like $sce.trustAsHtml() is getting the job done:

(function(angular) {
  'use strict';
angular.module('bindHtmlExample', ['ngSanitize'])
  .controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
    $scope.myHTML = $sce.trustAsHtml(
       "<em>Test</em> <input type='text' name='Hello'>");
  }]);
})(window.angular);

/*
Copyright 2018 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example - example-ng-bind-html-production</title>

  <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
  <script src="//code.angularjs.org/snapshot/angular-sanitize.js"></script>
  <script src="script.js"></script>
  
 
</head>
<body ng-app="bindHtmlExample">
  <div ng-controller="ExampleController">
    <p ng-bind-html="myHTML"></p>
  </div>
</body>
</html>

But keep in mind that you won't be able to use ng-model with an input rendered like that because you need to compile it first. You'll need a $compile service for that.

Serhii Holinei
  • 5,758
  • 2
  • 32
  • 46
  • This doesn't work as I am calling the tag from a JSON file and using a .filter to apply it. It is true, in my current HTML I am not calling it, for some odd reason I get a error message when I try using the filter. The error I get: Error "
    " angular.min.js:125
    – user5854648 Apr 06 '18 at 21:56
  • @user5854648 okay, this might be because you didn't register `trustAsHtml` filter correctly. – Serhii Holinei Apr 06 '18 at 22:01
  • This is the instance I use filter in the app: myApp.filter('trustAsHtml', [ '$sce', function($sce) { return function(value) { return $sce.trustAsHtml(value); } } ]); – user5854648 Apr 06 '18 at 22:04