AngularJS 1.4 指令:作用域、双向绑定和 bindToController

12

更新:肯定是代码的另一部分出了些愚蠢的问题。现在它正常工作了,因此 bindToController 语法没问题。

我们正在使用 AngularJS 1.4,该版本引入了指令中使用 bindToController 的 新方法

经过一番阅读(也可能并没有完全理解),我们将我们的指令定义为以下形式:

  .directive('mdAddress', function mdAddress() {
    var directive = {
      restrict: 'EA',
      scope: {},
      bindToController: {
        address: '='
      },
      templateUrl: 'modules/address/address.html',
      controller: AddressController,
      controllerAs: 'dir'
    };

像这样从另一个视图中调用它:

  <md-address address="vm.address"></md-address>

在视图控制器中先前定义为:

  vm.address = {
    street: null,
    countryCode: null,
    cityCode: null,
    postalCode: null
  };

像这样在指令模板中引用变量:

  <md-input-container>
    <label>{{'ADDRESSNUMBER' | translate}}</label>
    <input type="number" ng-model="dir.address.streetNumber">
  </md-input-container>

我们花了4个小时来弄清楚为什么我们的指令不起作用。嗯,它是在工作的,但是控制器和指令之间的双向绑定却没有起作用,vm.address.street无望地被设置为null。

一段时间后,我们只好试试旧的方式:

  .directive('mdAddress', function mdAddress() {
    var directive = {
      restrict: 'EA',
      scope: {
        address: '='
      },
      bindToController: true,
      templateUrl: 'modules/address/address.html',
      controller: AddressController,
      controllerAs: 'dir'
    };

这样就神奇地成功了。有什么想法为什么


你的 modules/address/address.html 模板中有什么内容?如果你在模板中尝试引用 vm,那么会出现空值问题,因为 vm 不存在于指令的作用域中。此外,如果你只是想在指令模板中绑定到 address,那也不会存在,因为你将控制器绑定为 dir,所以它必须是 dir.address - Joe Pontani
在模板中,我们使用 dir 引用了元素:<input type="number" ng-model="dir.address.streetNumber">。我编辑了主贴以澄清这一点。谢谢。 - Leo Lozes
请提供两个plunker,针对两种情况。虽然有简单的解释,但是纯文本无法清楚地说明您所做的具体内容。 - Estus Flask
我在升级到1.4+之前遇到了类似的问题。原始问题是否仍然引用较旧版本的Angular? - bingles
嗯,这是可能的,虽然我相当确定在尝试这个之前我们已经更改了 Angular 版本。现在很难说 :) - Leo Lozes
1个回答

20

更新:

感谢这篇博客文章的参考,我需要更新我的答案。自从AngularJS 1.4版本以来看起来你可以使用

scope: {},
bindToController: {
  variable: '='
}

这将做与旧语法完全相同的事情:

scope: {
  variable: '='
},
bindToController: true

用来解释这种行为的 AngularJS 源代码中有一些有用的行:

if (isObject(directive.scope)) {
  if (directive.bindToController === true) {
    bindings.bindToController = parseIsolateBindings(directive.scope,
                                                     directiveName, true);
    bindings.isolateScope = {};
  } else {
    bindings.isolateScope = parseIsolateBindings(directive.scope,
                                                 directiveName, false);
  }
}
if (isObject(directive.bindToController)) {
  bindings.bindToController =
      parseIsolateBindings(directive.bindToController, directiveName, true);
}
希望我能够解释为什么您经历的行为是正确的以及您在范围绑定概念上的误解。让我解释一下,您在第一段代码片段中做了什么:

来源:AngularJS 1.4.0

翻译后答案:

希望我能够解释为什么您经历的行为是正确的以及您在范围绑定概念上的误解。让我解释一下,您在第一段代码片段中做了什么:
.directive('mdAddress', function mdAddress() {
    var directive = {
      restrict: 'EA',
      scope: {},
      bindToController: {
        address: '='
      },
      templateUrl: 'modules/address/address.html',
      controller: AddressController,
      controllerAs: 'dir'
    };

使用 scope: {},您创建了一个隔离作用域(没有继承关系)用于您的 mdAddress 指令。这意味着:父控制器和您的指令之间不会传递任何数据。

考虑到这一点,关于您的第二段代码:

<md-address address="vm.address"></md-address>

vm.address 会从你的父控制器/视图中被分配为表达式到指令的 address 属性,但是由于你之前定义了一个隔离作用域,数据没有传递到 AddressController 中,因此在 bindToController 值中也不可用。

我们可以把 scope 对象定义看作“哪些数据将被传递”,而 bindToController 则是“哪些数据将在我的视图控制器对象中可用”的概念。

现在让我们来看一下最后(有效的)代码片段:

.directive('mdAddress', function mdAddress() {
    var directive = {
      restrict: 'EA',
      scope: {
        address: '='
      },
      bindToController: true,
      templateUrl: 'modules/address/address.html',
      controller: AddressController,
      controllerAs: 'dir'
    };

在这里,你创建了一个隔离的作用域(isolated scope),但是这次你添加了 address 属性以作为表达式传递。因此,你从第二个代码片段中从视图(view)传递的 address 将会在控制器(controller)的作用域中可用。现在设置 bindToController: true,将会将当前作用域的所有属性绑定到控制器上(或更可能是控制器别名对象)。现在,它按照你的期望工作,因为数据将被传递到作用域并且数据将被传递到控制器的模板作用域。

这个简要概述有助于你更好地理解 scopebindToController 的定义对象的概念吗?


1
确实,感谢您的回复。您是正确的,我需要撤销我的答案的某些部分。文章中提到的AngularJS源代码中的重要行是关键 - 因此在两种情况下行为应该是相同的。所以也许你的代码真的有一些奇怪的错误/打字错误?或者您能够从您的问题中重现所有内容吗?非常有趣的话题! - ConcurrentHashMap
谢谢,我觉得我可能误解了这篇文章 : )。无论如何,回顾AngularJS代码,scope: {}是否会将isObject返回为true,因为它是一个空对象,而不是null?那么它就会进入if语句的第一部分...我查看了AngularJS源代码中的isObject,但我不确定... - Leo Lozes
是的,你是对的,在这种情况下第一个if条件将会被评估为true(因为{}确实是一个对象)。在这个if语句中,else分支将会被触发。但是第二个外部if条件也将被评估为true,因为在你的第一个代码片段中directive.bindToController也是一个对象。所以bindings.bindToControllerbindings.isolateScope将被填充。目前还不知道它是否会遇到你遇到的这些问题。如果你仍然能够重现这个问题,那么了解一下会非常有趣! - ConcurrentHashMap
嗯,我必须说我很困惑。一定是某些非常愚蠢的事情。我们今天再次尝试了完全相同的方法,它奏效了。感谢您的帮助/时间,并为给您带来不便道歉。我发誓上周它没有起作用,但也许是我们在此期间修复的其他问题。毫无头绪。无论如何,bindToController不是问题所在。再次感谢! - Leo Lozes
1
那个简短的概述是否帮助您更好地理解作用域和bindToController定义对象的概念?对我来说确实有帮助! - Jimmy Kane
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接