【问题标题】:Two attribute directives with new/isolated scope on single element在单个元素上具有新/隔离范围的两个属性指令
【发布时间】:2016-10-03 12:14:59
【问题描述】:

我想使用 AngularJS 指令根据用户是否具有特定角色(作为属性值给出;每个元素可能不同)来禁用整个应用程序中表单的特定部分。该指令的代码如下:

/** @ngInject */
function PermissionDirective() {
    var directive = {
        restrict: 'A',
        scope: true,
        bindToController: true,
        controller: controller,
        controllerAs: 'permission',
        link: link
    };

    function link(scope, element, attrs) {
        scope.soPermission = attrs.soPermission;
    }

    return directive;
}

/** @ngInject */
function controller($scope, $attrs, ...) {
    var permission = this;
    permission.granted = false;
    $scope.soPermission = $attrs.soPermission;
    init();

    function init() {
        var requiredPermission = $scope.soPermission;

        permission.granted = // determine if user can use element
    }
}

请注意,它需要自己的作用域才能工作。 HTML中的用法是:

<button type="button" ng-disabled="... || !permission.granted" so-permission="WRITE">

只要so-permission 是元素中具有新范围的唯一指令,一切都很好。然而,一些元素也使用confirm 模态,这似乎也需要一个新的/隔离的范围。这会导致 Multiple directives... Angular 错误。

我的问题是:您如何解决这个问题?如何重新编写 so-permission 指令以摆脱内部作用域并使其继续工作(因为我无法触及 confirm 东西,因为它是一个库)?

【问题讨论】:

    标签: javascript angularjs angularjs-directive angularjs-scope


    【解决方案1】:

    您打算赋予表单元素哪些不同的权限?也许退后一步,从更简单的 HTML 角度看待它是可能的?

    您是否可以简单地使用带有函数的 ng-attr-readonly 分配 readonly 属性,而不是创建指令,以确定它是否应该存在。该函数可能存在于表单的控制器中。

    <input type="text" ng-attr-readonly="getPermissions()" />
    

    以及功能:

    this.getPermissions = function() {
      if (this.granted === 'READ') return undefined;
      return 'readonly';
    }
    

    这种方法适合你的情况吗?

    更新:将此函数变成工厂并重新使用它。

    angular.module("myApp").factory("PermissionsFactory",PermissionsFactory);
    function PermissionsFactory() {
    
      factory = {
        getPermissions: getPermissions
      }
      return factory;
    
      function getPermissions(ctrl) {
        if (ctrl.granted === 'READ') return undefined;
        return 'readonly';
      } 
    
    }
    

    然后,在你的控制器中(假设你已经注入它):

    this.getPermissions = PermissionsFactory.getPermissions
    

    最后,在你的 HTML 中,确保将控制器传递给函数,因为函数不再是控制器的一部分,需要控制器的范围来检查权限:

    <input type="text" ng-attr-readonly="getPermissions(permission)" />
    

    【讨论】:

    • 是的,放弃指令以支持控制器功能可能对我有用。不过我有一个疑问。这个函数必须在整个应用程序中使用,它由 ui-router 中的几个状态层组成。我想避免在每个控制器中重复该功能。你有解决方案吗?
    • 当然!把它变成一个工厂,然后在你需要的地方注入那个工厂。然后,您可以在一处更改函数本身,它将传播到整个应用程序的所有用途。我已经更新了答案。
    • 谢谢。不过,这是我撞到另一面墙的时候。用户拥有的权限取自 Web 服务。调用产生的承诺在 ui-router 的resolve 中得到解决。但是,当我们从控制器中分离函数时,我们不能再使用resolve。 Afaik,在 Angular 中没有任何合理的方法来强制解决一个承诺。只有promise.then() { ... }这个成语,这里没用,因为我们要马上得到函数值……
    • 根据代码,您可以简单地 return true 而不是在 resolve 块中返回承诺。如果您从 Web 服务获取权限,您可以简单地将参数添加到 PermissionsFactory,而不是控制器,您可以在其中传递来自 Web 服务的结果。因为我看不到你写的所有东西,所以我不知道这是否适合你。
    • 我接受这个答案,因为它让我找到了最令人满意的解决方案。感谢您的帮助!
    【解决方案2】:

    在 Angular js 中,我们最多可以在单个元素上使用一个独立的指令。我建议尝试装饰你的指令。查看这篇文章了解更多详情 Decorating Directives

    【讨论】:

      【解决方案3】:

      您应该使用 $parse 服务与范围进行通信: /

      ** @ngInject */
              function PermissionDirective() {
              var directive = {
              restrict: 'A',
              link: link
          };
      
      function link(scope, element, attrs) {
          var getter = $parse(attrs.soPermission);
          var setter = getter.setter;
          setter(scope, val); // set value to scope
          var value = getter(scope); //get value from scope
      }
      
      return directive;
      }
      

      您也可以使用 scope.$watch 来动态同步值。

      【讨论】:

      • 感谢您的回答。您能否解释一下它如何帮助解决我的问题,即 1)摆脱多个指令...错误,2)仍然能够将指令用于具有不同角色的多个元素?
      • 我的示例可以在没有自己的范围的情况下读取和编辑范围。它不会抛出范围错误
      • 是的。但是如果我理解正确的话,当我这样做时,该指令的一次出现将产生将被随后的出现覆盖的状态。
      • 我不知道,这与您的问题有何关系。这完全取决于您的代码组织。您可以使用 my-dir="vm.var1" 和 my-dir="vm.var2" 之类的指令,它们会将数据写入不同的变量。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-27
      • 1970-01-01
      • 2019-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多