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

Foo*_*bar 49 javascript angularjs angularjs-directive

我想创建一个指令,根据来自服务的值(例如,检查用户角色)检查dom中是否应该存在元素.

相应的指令如下所示:

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);
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

最后,元素附加了ng-if属性,但不知何故,它不适用于元素,它仍然存在于dom中.所以这显然是一种错误的做法.

这个小提琴显示了问题:http://jsfiddle.net/L37tZ/2/

谁能解释为什么会这样?有没有其他方式可以实现类似的行为?应考虑现有的ngIfs.

解:

用法: <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;
          }
        }
      });
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

在指令中添加优先级非常重要,否则不会评估附加到该元素的其他指令!

Jos*_*cha 80

您可以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);
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它

<div your-custom-if="true">This is shown</div>
Run Code Online (Sandbox Code Playgroud)

它将使用随附的所有"功能" ngIf.

  • 我认为这是一个比接受的答案更好的方法,因为它非常通用并使用"真正的"ngIf指令.没有指令代码重复,如果ngIf将来发生变化(可能是因为修正/改进),这将自动使用它. (3认同)

hil*_*ius 48

Joscha的答案相当不错,但实际上如果你使用ng-if,这将不起作用.我拿了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);
        }
    };
}]);
Run Code Online (Sandbox Code Playgroud)

那么如果可以做以下事情:

<input type="text" ng-model="test">
<div ng-if="test.length > 0" if-authenticated>Conditional div</div>
Run Code Online (Sandbox Code Playgroud)

div只有在您通过身份验证并且&测试输入不为空时,条件才会显示.

  • 在JS中,arguments是一个类似于数组的对象,包含函数的参数.这里,参数是`[scope,element,attributes,...]`where ...是angular传递给link函数的参数,即使它们没有注入到我自己的链接函数中.你可以在[mozzila docs]中找到解释和例子(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) (3认同)

Bri*_*sio 8

问题的第一部分"为什么?"是我能回答的问题:

您遇到的问题是您无法在不调用元素的情况下动态地将指令应用于元素$compile.

如果$compile(element)(element.scope())在设置属性后调用,则会遇到堆栈溢出,因为您正在编译自己,导致您自己编译,导致您自己编译等.

第二部分,"如何实现",我遇到了麻烦.我尝试了几种方法(比如用嵌套转换内容ng-if),但我无法得到你想要的行为.

我认为下一步可能是研究ng-if的代码并尝试在你的指令中直接实现类似的东西.

这是让它运转第一步.我希望它需要一些清理和修改才能让它按照你真正想要的方式工作.