0

I have a bit of a problem. I have a directive

app.directive('a', function() {
    return {
        restrict: 'E',
        link: function(scope, elem, attrs) {
            elem.on('click', function(e){
                e.preventDefault();
                alert('Hyperlinks not allowed!');
            });
        }
    };
});

and a $http request for JSON containing the page content

{
    "currentNodeName":"Page 1",
    "childrenNodes":[
        {"id":"3","name":"Page 1-1"},
        {"id":"4","name":"Page 1-2"}],
    "parentNode":null,
    "currentNodeContent":[
        {"content":"<p>This is Page1. <a href=\"http://badlink.org/i/dont/want/to/work\">Link</a></p>"}],
    "currentNodeId":"1"
}

The currentNodeContent is loaded to a div

<div id="loadedContent" ng-bind-html="boardCtrl.nodeData.currentNodeContent[0].content"></div>

and now the question: How can I achieve that the loaded content's <a> tag works as a directive?

Thanks.

Tomas Srna
  • 36
  • 2

2 Answers2

1

an almost correct asnwer can be found at angular ng-bind-html and directive within it although a better form would be:

app.directive("unsecureBind", function($compile) {
    return {
        link: function(scope, element, attrs) {
             scope.$watch(attrs.unsecureBind, function(newval) {
                  element.html(newval);
                  $compile(element.contents())(scope);
             });
        }   
    };  
});

The ng-bind-html simply assigns the data to the html without running $compile on it (see https://github.com/angular/angular.js/blob/master/src/ng/directive/ngBind.js#L197 ). This still isn't completely right becouse on change of the value the contained directives are not notified that they are being destroyed a better version would therefore be.

app.directive("unsecureBind", function($compile) {
    return {
        link: function(scope, element, attrs) {
            var childscope;
            scope.$watch(attrs.unsecureBind, function(newval, oldval) {
                if (!newval && !oldval) return; // handle first run
                if (childscope)
                    childscope.$destroy();
                element.html(newval || "");
                if (!newval) return;
                childscope = scope.$new();
                $compile(element.contents())(childscope);
            });
        }
    };
});

This is correct from the angular point of view, BUT:

  • you are completely violating the idea of mvc
  • by restricting the elements with directives you're basicaly blacklisting elements which is generaly not a good idea you should use a whitelist.
  • It is also very insecure to allow user input be part of your angular running context.

it would be a better idea to filter the input html through a whitelist function and then bind that with ng-bind-html.

Community
  • 1
  • 1
fghibellini
  • 636
  • 4
  • 15
0

Angular is using 'a' as a directive as priority '0' (https://docs.angularjs.org/api/ng/directive/a)

Try doing this:

  • set your priority higher than what was defined, example 1 or Number.MAX_VALUE.
  • set terminal to true, so it won't process the lower priorities.

could work, i guess... :)

sss
  • 1,259
  • 9
  • 23