AngularJs:使用Angular Directives传递跨多个隔离范围的函数参数

时间:2015-04-21 04:29:19

标签: javascript angularjs

我有3个嵌套的,隔离范围,指令(see CodePen here),我能够将一个函数(带参数)从最外层的指令传递给最里面的指令(将函数从外部指令传递给中间体)内部指令的指令)。

我无法理解的是将内部指令中的参数通过中间指令传递回外部指令需要做些什么。

再次,请参阅the CodePen example

注意:鉴于只有2个隔离范围指令,我可以使用类似于以下内容的方法...

angular.module('myApp', [])
  .directive('myDir1', function() {
    return {
      template: '<div><my-dir-2 add-name="addName(name)"></my-dir-2></div>',
      controller: function($scope) {
        $scope.addName = function(name) {
          alert(name); // alerts 'Henry'
        };
      }
    }
  })
  .directive('myDir2', function() {
    return {
      scope: {
        addName: '&'
      },
      template: "<span ng-click='addName({name: testName})'>Click to Add {{testName}}!</span>",
      controller: function($scope) {
        $scope.testName = 'Henry';
      }
    }
  });

上面的代码为我提供了一个警告框,其中包含&#39; Henry&#39; (就像我期待的那样)。

当我添加第三个中间隔离范围指令时,我遇到了问题......

angular.module('myApp', [])
  .directive('myDir1', function() {
    return {
      template: '<div><my-dir-2 add-name="addName(name)"></my-dir-2></div>',
      controller: function($scope) {
        $scope.addName = function(name) {
          alert(name); // alerts 'Henry'
        };
      }
    }
  })
  .directive('myDir2', function() {
    return {
      scope: {
        addName: '&'
      },
      template: '<div><my-dir-3 add-name="addName({name: testName})"></my-dir-3></div>',
    }
  })
  .directive('myDir3', function() {
    return {
      scope: {
        addName: '&'
      },
      template: "<span ng-click='addName({name: testName})'>Click to Add {{testName}}!</span>",
      controller: function($scope) {
        $scope.testName = 'Henry';
      }
    }
  });

此代码为我提供了一个警告框undefined ...

4 个答案:

答案 0 :(得分:22)

一个常见的误解是“&amp;是用于传递函数”。这在技术上是不正确的。

&所做的是在指令范围内创建函数,在调用时,返回针对父范围计算的表达式的结果。

此函数将一个对象作为参数,该参数将覆盖表达式中的局部变量,并使用指令范围({name: testName})对象中的局部变量。

如果你要深入了解,$scope.addName中的myDir2方法看起来像这样(简化):

$scope.addName = function(locals) {
    return $parse(attr.addName)($scope.$parent, locals);
}

你的第二个指令有效,因为它绑定的表达式是

addName(name)

此表达式具有局部变量name,当使用

执行时,该变量用指令中的testName值覆盖
addName({name: testName}) //in the directive. 

请记住 - addName中的myDir2功能与addName中的myDir1功能不同。它是一个评估表达式

的新函数
addName(name) 

针对父作用域并返回结果。

将此逻辑应用于myDir3时,评估的表达式为:

addName({name: testName})

请注意,此表达式中唯一的局部变量是“testName”。因此,当您使用

拨打myDir3
addName({name: testName})

没有要覆盖的局部变量name,并且testName未定义。

唷!难怪这只会让每个人都感到困惑!

如何解决您的示例:

您希望表达式计算为myDir1中的实际函数。

angular.module('myApp', [])
  .directive('myDir1', function() {
    return {
      template: '<div><my-dir-2 add-name="addName"></my-dir-2></div>',
      controller: function($scope) {
        $scope.addName = function(name) {
          alert(name); // alerts 'Henry'
        };
      }
    }
  })
  .directive('myDir2', function() {
    return {
      scope: {
        addName: '&'
      },
      // addName() returns the actual function addName from myDir1
      template: '<div><my-dir-3 add-name="addName()"></my-dir-3></div>',
    }
  })
  .directive('myDir3', function() {
    return {
      scope: {
        addName: '&'
      },
      //calls addName() on myDir2, which returns addName from myDir1, then invokes it passing testName as an argument
      template: "<span ng-click='addName()(testName)'>Click to Add {{testName}}!</span>",
      controller: function($scope) {
        $scope.testName = 'Henry';
      }
    }
  });

Here is the working Pen

最后的注释 - '&''='更合适的原因是'='实际上会设置$watch并且双向绑定变量指令之间。这意味着myDir2实际上可以更改appName中的myDir1函数,这不是必需的,也不是必需的。它还需要设置两个$watch,我试图在Angular中出于性能原因避免这种情况。

答案 1 :(得分:4)

在隔离范围内传递函数有两种方法。虽然&#39;&amp;&#39;将确保您传递的内容实际上是一个函数,您还可以将函数作为绑定变量传递给&#39; =&#39;,并仅在您需要时调用它。这种方法有缺点,但它会将调用的控制权交给负责该调用的组件。

Your codepen working

angular.module('myApp', [])
 .directive('myDir1', function() {
    return {
      template: '<div><my-dir-2 add-name="addName"></my-dir-2></div>',
      controller: function($scope) {
        $scope.addName = function(name) {
        alert(name);
      };
    }
  }
})
.directive('myDir2', function() {
  return {
    scope: {
      addName: '='
    },
    template: '<div><my-dir-3 add-name="addName"></my-dir-3></div>'
  }
})
.directive('myDir3', function() {
  return {
    scope: {
      addName: '='
    },
    template: "<span ng-click='addName(testName)'>Click to Add {{testName}}!</span>",      
    controller: function($scope) {
      $scope.testName = "Henry"
    }
  }
});

答案 2 :(得分:0)

具有三个嵌套指令的代码存在的问题是testNamemyDir2的隔离范围内不存在myDir3。您可以从myDir2设置,但为此您必须在myDir2范围内创建一个对象,并将其属性设置为testName,然后在{{1}}中使用该属性

工作示例就在这里 http://codepen.io/anon/pen/Wveqwx

答案 3 :(得分:-1)

您可以使用isolateScope()

访问元素范围

myDir1指令中,将controller替换为

  link: function($scope, elem) {
    $scope.addName = function(name) {
      var e = elem.find('my-dir-3');
      var s = e.isolateScope();
      alert(s.testName);
    };

另一种方法是可能使用$$nextSibling来获取当前父级之后的下一个范围(没有真正使用它,所以可能想要阅读它)

这可能不是“有角度的方式”&#39;做事。我认为从子指令中删除隔离范围并让它们引用父指令模型会更理想。