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.

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

Comments

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... :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.