8

为了掌握 AngularJS,我决定尝试其中一个示例,具体来说,只需向 Todo 示例添加一个“完整”屏幕,当用户输入 5 个待办事项时,它使用 switch-case 切换到另一个 div . 如果有任何用处,可在此处获取代码http://jsfiddle.net/FWCHU/1/ 。

但是,似乎每个 switch-case 都有自己的范围($scope.todoText 不可用),但是在这种情况下,可以使用 addTodo() 中的“this”访问它。到目前为止一切顺利,但是说我想在 switch-case 之外访问 todoText(在 switch-case 内部),我该怎么做呢?我是否可以将 switch-case 范围绑定到模型,它是否可以通过其他方式访问,或者应该以其他方式解决?

PS。我不想上面的代码找到任何解决方案,我很确定我知道如何在不使用 switch-cases 的情况下解决它,我想了解作用域在这种情况下是如何工作的!

4

2 回答 2

8

马克有一些很棒的建议!确保您还检查了AngularJS Batarang Chrome 扩展以查看各种范围及其值(以及其他内容)。请注意,它似乎不适用于 jsFiddle。

我不确定如何直接访问内部范围,但这是通过绑定到对象而不是原语来访问外部范围中相同文本的一种方法。

1)todoText在控制器中声明为对象而不是原语:

$scope.todoText = {text: ''};

2)绑定到todoText.text而不是仅仅todoText

<form ng-submit="addTodo()">
    <input type="text" ng-model="todoText.text" size="30" placeholder="add new todo here">
    <input class="btn-primary" type="submit" value="add">
</form>

3)修改现有功能以使用todoText.text

$scope.addTodo = function() {
    $scope.todos.push({text:$scope.todoText.text, done:false, width: Math.floor(Math.random() * 100) + 50});
    $scope.todoText.text = '';
};

看看这个 fiddle并注意当您输入内容时显示在文本框下方的文本正在访问todoText.text外部范围。

如果您将代码更改回使用原语(如在此小提琴中),则父范围todoText不会反映您对文本框所做的任何更改。这可能更多地与 JavaScript 如何复制引用值有关(有关更多信息,请参阅这篇文章),而不是 AngularJS 特定的事情。

于 2012-09-13T21:42:16.887 回答
5

更新 2: 现在我对 AngularJS 有了更多的了解,这是一个更好的答案。

假设我想在 switch-case 之外访问 todoText(在 switch-case 内部),我该怎么做呢?

父作用域无法访问子作用域。(根据 Angular 开发人员的说法,这种限制的一个原因是为了更容易地对范围进行内存管理。)(嗯,你可以使用 $$childHead 和 $$childTail 来访问子范围,但你不应该这样做!)

我是否可以将 switch-case 范围绑定到模型,它是否可以通过其他方式访问,或者应该以其他方式解决?

从子作用域访问父模型的常用方法有以下三种:

  1. 执行@Gloopy 建议的操作:在父范围内创建一个对象,然后在子范围内引用该对象的属性。
  2. 在子作用域中使用 $parent 来访问父作用域及其属性,甚至是原始属性。
  3. 在父作用域上调用方法

要将您的小提琴转换为使用 $parent:

<input type="text" ng-model="$parent.todoText" ...

$scope.addTodo = function() {
   $scope.todos.push({text: $scope.todoText, ...
   $scope.todoText = '';

正如我在 Gloopy 答案的评论中提到的那样,ng-repeat 和 ng-switch 都有新的子作用域原型继承自父作用域。ng-repeat 还将循环变量/项目复制到新的子范围(以及@Gloopy 用原语与对象描述的细微差别适用)。ng-switch 不会从父范围复制任何内容。

要查看内部/子范围的外观,请在 ng-switch-when 之后添加以下内容:

<a ng-click="showScope($event)">show scope</a>

并将其添加到您的控制器:

$scope.showScope = function(e) {
    console.log(angular.element(e.srcElement).scope());
}

更新1:(删除线添加到不好的建议,[]为清楚起见添加)

对于这种情况,AngularJS 正在创建额外的内部范围(隐式),而您并不真正想要/需要另一个控制器,我喜欢 Gloopy 的解决方案。服务(我最初在下面建议的)是 [执行此操作的错误方法]在这里可能有点矫枉过正。我也喜欢 Gloopy 的解决方案不需要在控制器方法中使用“this”。

原始答案:(为不良建议添加了删除线,为清楚起见添加了[])

要查看创建范围的位置(如果您还没有尝试过,这很方便):

.ng-scope { margin: 4px; border: 1px dashed red }

要在 switch-case 之外访问 todoText(因此在其范围之外),您实际上是在询问控制器间通信,因为涉及多个范围。您有几个选择,但服务可能是最好的。将数据(需要共享的)存储在服务中,并将该服务注入需要访问数据的每个控制器。

对于您的具体示例,我认为您需要将一个控制器附加到每个开关盒并将服务注入其中,以访问共享数据。

另请参阅AngularJS:如何在控制器之间传递变量?.

其他选项:

不推荐在内部范围内使用 $scope.$parent [执行此操作的一种方法——参见上面的 Update2] ,因为那时控制器将对数据的呈现方式做出假设。

不建议使用 $rootScope,除非可能用于简单的一次性应用程序。共享数据可能会开始拥有自己的生命,而 $rootScope 不是发生这种情况的地方。服务更容易重用、添加行为等。

使用 $scope.$emit 是另一种选择,但它看起来很混乱而且有点奇怪:发出事件来共享数据(而不是触发行为)。

[在父范围内使用对象可能是最好的——请参阅@Gloopy 的答案。]

于 2012-09-13T20:36:58.957 回答