简体   繁体   English

AngularJS:以编程方式将ngIf添加到指令的最佳做法是什么?

[英]AngularJS: What's the best practice to add ngIf to a directive programmatically?

I want to create a directive that checks if an element should be present in the dom based on a value coming from a service (eg check for a user role). 我想创建一个指令,根据来自服务的值(例如,检查用户角色)检查dom中是否应该存在元素。

The corresponding directive looks like this: 相应的指令如下所示:

angular.module('app', []).directive('addCondition', function($rootScope) {
    return {
        restrict: 'A',
        compile: function (element, attr) {
          var ngIf = attr.ngIf,
              value = $rootScope.$eval(attr.addCondition);

          /**
           * Make sure to combine with existing ngIf!
           * I want to modify the expression to be evalued by ngIf here based on a role 
           * check for example
           */
          if (ngIf) {
            value += ' && ' + ngIf;
          }

          attr.$set('ng-if', value);
        }
    };
});

At the end the element has the ng-if attribute attached but somehow it doesn't apply to the element and it is still existing in the dom. 最后,元素附加了ng-if属性,但不知何故,它不适用于元素,它仍然存在于dom中。 So this is obviously a wrong approach. 所以这显然是一种错误的做法。

This fiddle shows the problem: http://jsfiddle.net/L37tZ/2/ 这个小提琴显示了问题: http//jsfiddle.net/L37tZ/2/

Who can explain why this happens? 谁能解释为什么会这样? Is there any other way a similar behaviour could be achieved? 有没有其他方式可以实现类似的行为? Existing ngIfs should be considered. 应考虑现有的ngIfs。

SOLUTION: 解:

Usage: <div rln-require-roles="['ADMIN', 'USER']">I'm hidden when theses role requirements are not satifisfied!</div> 用法: <div rln-require-roles="['ADMIN', 'USER']">I'm hidden when theses role requirements are not satifisfied!</div>

.directive('rlnRequireRoles', function ($animate, Session) {

  return {
    transclude: 'element',
    priority: 600,
    terminal: true,
    restrict: 'A',
    link: function ($scope, $element, $attr, ctrl, $transclude) {
      var block, childScope, roles;

      $attr.$observe('rlnRequireRoles', function (value) {
        roles = $scope.$eval(value);
        if (Session.hasRoles(roles)) {
          if (!childScope) {
            childScope = $scope.$new();
            $transclude(childScope, function (clone) {
              block = {
                startNode: clone[0],
                endNode: clone[clone.length++] = document.createComment(' end rlnRequireRoles: ' + $attr.rlnRequireRoles + ' ')
              };
              $animate.enter(clone, $element.parent(), $element);
            });
          }
        } else {

          if (childScope) {
            childScope.$destroy();
            childScope = null;
          }

          if (block) {
            $animate.leave(getBlockElements(block));
            block = null;
          }
        }
      });
    }
  };
});

It is very important to add the priority in the directive, otherwise other directives attached to that element are not evaluated! 在指令中添加优先级非常重要,否则不会评估附加到该元素的其他指令!

You can reuse ngIf in your own directive like this: 您可以在自己的指令中重复使用ngIf ,如下所示:

/** @const */ var NAME = 'yourCustomIf';

yourApp.directive(NAME, function(ngIfDirective) {
  var ngIf = ngIfDirective[0];

  return {
    transclude: ngIf.transclude,
    priority: ngIf.priority,
    terminal: ngIf.terminal,
    restrict: ngIf.restrict,
    link: function($scope, $element, $attr) {
      var value = $attr[NAME];
      var yourCustomValue = $scope.$eval(value);

      $attr.ngIf = function() {
        return yourCustomValue;
      };
      ngIf.link.apply(ngIf, arguments);
    }
  };
});

and then use it like this 然后像这样使用它

<div your-custom-if="true">This is shown</div>

and it will use all the "features" that come with using ngIf . 它将使用ngIf带来的所有“功能”。

Joscha's answer is pretty good, but actually this won't work if you're using ng-if in addition of it. Joscha的答案相当不错,但实际上如果你使用ng-if,这将不起作用。 I took Joscha's code and just added a few lines to combine it with existing ng-if directives : 我拿了Joscha的代码并添加了几行来将它与现有的ng-if指令结合起来:

angular.module('myModule').directive('ifAuthenticated', ['ngIfDirective', 'User', function(ngIfDirective, User) {
    var ngIf = ngIfDirective[0];

    return {
        transclude: ngIf.transclude,
        priority: ngIf.priority - 1,
        terminal: ngIf.terminal,
        restrict: ngIf.restrict,
        link: function(scope, element, attributes) {
            // find the initial ng-if attribute
            var initialNgIf = attributes.ngIf, ifEvaluator;
            // if it exists, evaluates ngIf && ifAuthenticated
            if (initialNgIf) {
                ifEvaluator = function () {
                    return scope.$eval(initialNgIf) && User.isAuthenticated();
                }
            } else { // if there's no ng-if, process normally
                ifEvaluator = function () {
                    return User.isAuthenticated();
                }
            }
            attributes.ngIf = ifEvaluator;
            ngIf.link.apply(ngIf, arguments);
        }
    };
}]);

So if can then do things like : 那么如果可以做以下事情:

<input type="text" ng-model="test">
<div ng-if="test.length > 0" if-authenticated>Conditional div</div>

And the conditional div will show only if you're authenticated && the test input is not empty. 只有在您通过身份验证并且&测试输入不为空时,条件div才会显示。

The first part of your question, "why?", is something I can answer: 问题的第一部分“为什么?”是我能回答的问题:

The problem you are running into is that you can't dynamically apply directives to elements without calling $compile on the element. 您遇到的问题是,如果不在元素上调用$compile ,则无法动态地将指令应用于元素。

If you call $compile(element)(element.scope()) after you set the attribute, you run into a stack overflow because you are compiling yourself, which cause you to compile yourself which causes you to compile yourself, etc. 如果在设置属性后调用$compile(element)(element.scope()) ,则会遇到堆栈溢出,因为您正在编译自己,导致您自己编译,导致您自己编译等。

The second part, "how else to achieve", I am having trouble with. 第二部分,“如何实现”,我遇到了麻烦。 I tried a couple of approaches (like transcluding the content with a nested ng-if ) but I can't get exactly the behavior you are looking for. 我尝试了几种方法(比如使用嵌套的ng-if ),但我无法准确地获得您正在寻找的行为。

I think the next step might be to study the code for ng-if and try to implement something similar directly in your directive. 我认为下一步可能是研究ng-if的代码并尝试在你的指令中直接实现类似的东西。

Here is a first pass of getting it working . 这是让它运转第一步 I expect it needs some cleanup and modification to get it working how you really want it, however. 我希望它需要一些清理和修改才能让它按照你真正想要的方式工作。

There is another way to solve this problem, using a templating function. 还有另一种方法可以使用模板功能来解决这个问题。 This requires jquery 1.6+ to function properly. 这需要jquery 1.6+才能正常运行。

A working fiddle of the code: http://jsfiddle.net/w72P3/6/ 代码的工作小提琴: http//jsfiddle.net/w72P3/6/

return {
    restrict: 'A',
    replace: true,
    template: function (element, attr) {
        var ngIf = attr.ngIf;
        var value = attr.addCondition;
        /**
         * Make sure to combine with existing ngIf!
         */
        if (ngIf) {
            value += ' && ' + ngIf;
        }
        var inner = element.get(0);
        //we have to clear all the values because angular
        //is going to merge the attrs collection 
        //back into the element after this function finishes
        angular.forEach(inner.attributes, function(attr, key){
            attr.value = '';
        });
        attr.$set('ng-if', value);
        return inner.outerHTML;            
    }
}

replace: true prevents embedded elements. replace:true可防止嵌入元素。 Without replace=true the string returned by the template function is put inside the existing html. 如果没有replace = true,模板函数返回的字符串将放在现有的html中。 Ie <a href="#" addCondition="'true'">Hello</a> becomes <a href="#" ng-if="'true'"><a href="#" ng-if="'true'">Hello</a></a> <a href="#" addCondition="'true'">Hello</a>成为<a href="#" ng-if="'true'"><a href="#" ng-if="'true'">Hello</a></a>

See https://docs.angularjs.org/api/ng/service/ $compile for details. 有关详细信息,请参阅https://docs.angularjs.org/api/ng/service/ $ compile。

return {
    restrict: 'A',
    terminal: true,
    priority: 50000, // high priority to compile this before directives of lower prio
    compile: function compile(element, attrs) {
        element.removeAttr("add-condition"); // avoid indefinite loop
        element.removeAttr("data-add-condition");

        return {
            pre: function preLink(scope, iElement, iAttrs, controller) {  },
            post: function postLink(scope, iElement, iAttrs, controller) { 
                iElement[0].setAttribute('ng-if', iAttrs.addCondition);
                $compile(iElement)(scope);
            }
        };
    }

The combination of high priority and terminal: true is the basis how this works: The terminal flag tells Angular to skip all directives of lower priority on the same HTML element. 高优先级和terminal: true的组合terminal: true是这样工作的基础:终端标志告诉Angular在同一个HTML元素上跳过所有低优先级的指令。

This is fine because we want to modify the element by replacing add-condition with ng-if before calling compile , which then will process ng-if and any other directives. 这很好,因为我们想要在调用compile之前用ng-if替换add-condition来修改元素,然后将处理ng-if和任何其他指令。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在AngularJS的指令隔离范围中使用方法是最佳实践吗? - Is it a best practice to have a method in AngularJS's directive isolated scope? AngularJS在指令中继承范围的最佳实践 - AngularJS best practice to inherit scope in directive 扩展angularjs指令功能的最佳方法是什么? - What's the best way to extend angularjs directive functionality? 在AngularJS中处理应用程序状态和事件广播的最佳实践是什么? - What's the best practice for dealing with application states and event broadcasting in AngularJS? 使用append将标签添加到数组中的最佳实践是什么? jQuery的 - what's the best practice to add tags into the array using append? jQuery Angular指令的控制器中的this.ngIf有什么作用? - What does this.ngIf in an Angular directive's controller do? AngularJS最佳实践:基于对象数据“更改模板”的最佳方法是什么? - AngularJS Best Practice: What's the best way to 'change templates' based on object data? 重新加载AngularJS页面的最佳实践是什么 - what is the best practice to reload AngularJS page 在给定的工作流程中,angularjs和指令的最佳实践是什么? - What is the best practice for angularjs and directives in the given workflow? AngularJS中多个$ http请求的最佳实践是什么? - What is the best practice for multiple $http request in AngularJS?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM