No1Lives4Ever
No1Lives4Ever

Reputation: 6903

AngularJS: Compile specific template

I have a pre tag with dynamic text (unknown while the page is being loaded). This text may have ng commands. It will looks like this:

<pre>
    Hello <span ng-click="test('args')">world</span> angular-JS!
</pre>

Because those dynamic tags are not exists while the page is being loaded, angularjs is not run them and the function test('args') will never run.

So, I'm added a directive called compile-template:

function compileTemplate($compile, $parse)
{
    return {
        link: function (scope, element, attr)
        {
            var parsed = $parse(attr.ngBindHtml);
            function getStringValue() { return (parsed(scope) || '').toString(); }

            //Recompile if the template changes
            scope.$watch(getStringValue, function ()
            {
                $compile(element, null, -9999)(scope);  //The -9999 makes it skip directives so that we do not recompile ourselves
            });
        }
    }
}

And now my pre tag is looks like this:

<pre compile-template>
    Hello <span ng-click="test('args')">world</span> angular-JS!
</pre>

This is working good.

The problem starts when my text my text change for something like:

<pre compile-template>
    Hello <span ng-click="test('args')">world</span> angular-JS! }}
</pre>

When I tried to compile this text ("Hello world angular-JS! }}") I getting this error:

Error: [$parse:lexerr] http://errors.angularjs.org/1.5.8/$parse/lexerr?p0=Unexpected%20next%20character%20&p1=s%200-0%20%5B%23%5D&p2=%23coordinates%3A21%7C37%7CN%7C87%7C04%7CW%7C%7C%7C%0A%0A%0A%7C%0A%7Cname%3D
    at angular.js:38
    at jc.throwError (angular.js:14179)
    at jc.lex (angular.js:14101)
    at s.ast (angular.js:14303)
    at td.compile (angular.js:14771)
    at kc.parse (angular.js:15700)
    at g (angular.js:15865)
    at k (angular.js:12364)
    at ca (angular.js:9724)
    at $b (angular.js:8743)

This because the "}}" string which is associated with broken JS code.

Example: https://jsfiddle.net/m69q87eg/

So, basicly what I tring to do is to allow <span>s with JS, but nothing else.

I though to move the compile-template directive to be part of my span. Like this:

<pre>
    Hello <span compile-template ng-click="test('args')">world</span> angular-JS!
</pre>

But it's isn't working since the pre outter HTML is treated like text.

What to do?

Upvotes: 4

Views: 513

Answers (1)

Stanislav Kvitash
Stanislav Kvitash

Reputation: 4622

You can try using custom ngBindHtmlCompile directive which will $compile only the tags that were provided in the attributes (using querySelectorAll() method or some other filtering logic of your choice):

var module = angular.module("demo", []);

module.directive('ngBindHtmlCompile', ['$compile', '$parse', function ($compile, $parse) {
  return {
    link: function (scope, element, attr) {
      var parsed = $parse(attr.ngBindHtmlCompile);
      function getStringValue() { return (parsed(scope) || '').toString(); }
      scope.$watch(getStringValue, function (val) {
        element.html(val);
        if (!!attr.compileTags) {
          $compile(element[0].querySelectorAll(attr.compileTags))(scope); 
        } else {
          $compile(element, null, -9999)(scope); 
        }          
      });
    }
  }
}]);

module.controller('Demo', ['$scope', '$window', function Demo($scope, $window) {
    var vm = this; 
    
    vm.template1 = `
populations of Eurasia <span>{{vm.untilStr}}</span> 1492, when Christopher Columbus first <span class=\"highlight-1\" ng-click=\"vm.test(2810)\">sailed into Caribbean waters on a quest to find a sea route to Asia. At that time the Western Hemisphere in general was unknown to Europeans. Following the discovery of the islands by Columbus, the area was quickly colonised by several Western cultures (initially Spain, then later</span>

In the Yucatan Channel. The same limit as that described for the Gulf of Mexico [A line joining Cape Catoche Light (21°37′N 87°04′W / 21.617°N 87.067°W / 21.617; -87.067{{#coordinates:21|37|N|87|04|W|||


|
|name=
}})  -- 1213123 <span class=\"highlight-1\" ng-click=\"vm.test(4117)\">and the extreme of Agujereada </span>`;
     
    vm.test = function(args){ $window.alert(args); };
    vm.untilStr = "until";
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>

<div ng-app="demo" ng-controller="Demo as vm">   
    <pre ng-bind-html-compile="vm.template1" compile-tags="span"></pre>         
    <br/>
</div>

Upvotes: 1

Related Questions