最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Modify template in directive (dynamically adding another directive) - Stack Overflow

programmeradmin3浏览0评论

Problem

Dynamically add the ng-bind attribute through a custom directive to be able to use ng-bind, ng-bind-html or ng-bind-html-unsafe in a custom directive with out manually adding to the template definition everywhere.

Example

/

Broken Directive

angular.module('app').directive('bindTest', [
'$pile',
function ($pile) {
    return {
        restrict: 'A',
        scope: true,
        pile: function (tElem, tAttrs) {
            if (!tElem.attr('ng-bind')) {
                tElem.attr('ng-bind', 'content');
                $pile(tElem)
            }
            return function (scope, elem, attrs) {
                console.log('Linking...');
                scope.content = "Content!";
            };
        }
    };
}]);

Solution

No idea. Really I can not figure out why something like the above fiddle doesn't work. Tried it with and with out the extra $pile in there.

Workaround

I can work around it might adding a template value in the directive, but that wraps the content in an extra div, and I would like to be able to that if possible. (See fiddle)

Second Workaround

See the fiddle here: / (as suggested by Dr. Ikarus below). I'm considering this a workaround for right now, because it still feels like you should be able to modify the template before you get to the linking function and the changes should be found/applied.

Problem

Dynamically add the ng-bind attribute through a custom directive to be able to use ng-bind, ng-bind-html or ng-bind-html-unsafe in a custom directive with out manually adding to the template definition everywhere.

Example

http://jsfiddle/nstuart/hUxp7/2/

Broken Directive

angular.module('app').directive('bindTest', [
'$pile',
function ($pile) {
    return {
        restrict: 'A',
        scope: true,
        pile: function (tElem, tAttrs) {
            if (!tElem.attr('ng-bind')) {
                tElem.attr('ng-bind', 'content');
                $pile(tElem)
            }
            return function (scope, elem, attrs) {
                console.log('Linking...');
                scope.content = "Content!";
            };
        }
    };
}]);

Solution

No idea. Really I can not figure out why something like the above fiddle doesn't work. Tried it with and with out the extra $pile in there.

Workaround

I can work around it might adding a template value in the directive, but that wraps the content in an extra div, and I would like to be able to that if possible. (See fiddle)

Second Workaround

See the fiddle here: http://jsfiddle/nstuart/hUxp7/4/ (as suggested by Dr. Ikarus below). I'm considering this a workaround for right now, because it still feels like you should be able to modify the template before you get to the linking function and the changes should be found/applied.

Share Improve this question edited Jun 20, 2020 at 9:12 CommunityBot 11 silver badge asked Mar 11, 2013 at 23:26 nick.stuartnick.stuart 5441 gold badge7 silver badges18 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 2

You could do the piling part inside the linking function, like this:

angular.module('app').directive('bindTest', ['$pile', function ($pile) {
    return {
        restrict: 'A',
        scope: true,
        link: {
            post: function(scope, element, attrs){
                if (!element.attr('ng-bind')) {
                    element.attr('ng-bind', 'content');
                    var piledElement = $pile(element)(scope);
                }
                console.log('Linking...');
                scope.content = "Content!";                
            }
        }
    };
}]);

Let me know how well this worked for you http://jsfiddle/bPCFj/

This way seems more elegant (no dependency with $pile) and appropriate to your case :

angular.module('app').directive('myCustomDirective', function () {
    return {
        restrict: 'A',
        scope: {},
        template: function(tElem, tAttrs) {
            return tAttrs['ng-bind'];
        },
        link: function (scope, elem) {
            scope.content = "Happy!";
        }
    };
});

jsFiddle : http://jsfiddle/hUxp7/8/

From Angular directive documentation : You can specify template as a string representing the template or as a function which takes two arguments tElement and tAttrs (described in the pile function api below) and returns a string value representing the template.

The source code tells all! Check out the pileNodes() function and its use of collectDirectives().

First, collectDirectives finds all the directives on a single node. After we've collected all the directives on that node, then the directives are applied to the node.

So when your pile function on the bindTest directive executes, the running $pile() is past the point of collecting the directives to pile.

The extra call to $pile in your bindTest directive won't work because you are not linking the directive to the $scope. You don't have access to the $scope in the pile function, but you can use the same strategy in a link function where you do have access to the $scope

You guys were so close.

function MyDirective($pile) {
    function pileMyDirective(tElement) {
        tElement.attr('ng-bind', 'someScopeProp');

        return postLinkMyDirective;
    }

    function postLinkMyDirective(iScope, iElement, iAttrs) {
        if (!('ngBind' in iAttrs)) {
            // Before $pile is run below, `ng-bind` is just a DOM attribute
            // and thus is not in iAttrs yet.
            $pile(iElement)(iScope);
        }
    }

    var defObj = {
        pile: pileMyDirective,
        scope: {
            someScopeProp: '=myDirective'
        }
    };

    return defObj;
}

The result will be:

<ANY my-directive="'hello'" ng-bind="someScopeProp">hello</ANY>
发布评论

评论列表(0)

  1. 暂无评论