AngularJS控制器中的'this'与$ scope

时间:2012-07-23 02:55:48

标签: angularjs angularjs-scope this

"Create Components" section of AngularJS's homepage中,有一个例子:

controller: function($scope, $element) {
  var panes = $scope.panes = [];
  $scope.select = function(pane) {
    angular.forEach(panes, function(pane) {
      pane.selected = false;
    });
    pane.selected = true;
  }
  this.addPane = function(pane) {
    if (panes.length == 0) $scope.select(pane);
    panes.push(pane);
  }
}

请注意select方法如何添加到$scope,但addPane方法已添加到this。如果我将其更改为$scope.addPane,则代码会中断。

文档说实际上存在差异,但没有提到差异是什么:

  

以前版本的Angular(1.0之前的RC)允许您与this方法交替使用$scope,但现在不再是这种情况了。在范围this$scope上定义的方法内部是可互换的(角集this$scope),但在控制器构造函数中没有。

this$scope如何在AngularJS控制器中工作?

7 个答案:

答案 0 :(得分:980)

  

this$scope如何在AngularJS控制器中运行?”

简短回答

  • this
    • 调用控制器构造函数时,this是控制器。
    • 当调用在$scope对象上定义的函数时,this是“调用函数时有效的范围”。这可能(或可能不是!)是定义函数的$scope。因此,在函数内部,this$scope可能相同。
  • $scope
    • 每个控制器都有一个关联的$scope对象。
    • 控制器(构造函数)函数负责在其关联的$scope上设置模型属性和函数/行为。
    • 只能在HTML /视图中访问此$scope对象上定义的方法(以及父范围对象,如果是原型继承)。例如,来自ng-click,过滤器等

答案很长

控制器功能是JavaScript构造函数。当构造函数执行时(例如,当视图加载时),this(即,“函数上下文”)被设置为控制器对象。所以在“tabs”控制器构造函数中,当创建addPane函数时

this.addPane = function(pane) { ... }

它是在控制器对象上创建的,而不是在$ scope上创建的。视图无法看到addPane函数 - 它们只能访问$ scope上定义的函数。换句话说,在HTML中,这将不起作用:

<a ng-click="addPane(newPane)">won't work</a>

执行“tabs”控制器构造函数后,我们有以下内容:

after tabs controller constructor function

黑色虚线表示原型继承 - 原型继承自Scope的隔离范围。 (它没有原型地继承HTML中遇到指令的有效范围。)

现在,窗格指令的链接函数想要与tabs指令进行通信(这实际上意味着它需要以某种方式影响选项卡隔离$ scope)。可以使用事件,但另一种机制是使窗格指令require成为制表符控制器。 (对于require选项卡$ scope,窗格指令似乎没有机制。)

所以,这就引出了一个问题:如果我们只能访问制表符控制器,那么我们如何访问制表符隔离$ scope(这是我们真正想要的)?

嗯,红色虚线就是答案。 addPane()函数的“范围”(我指的是JavaScript的函数范围/闭包)给函数访问选项卡隔离$ scope。即,addPane()可以访问上图中的“tabs IsolateScope”,因为在定义addPane()时创建了一个闭包。 (如果我们在选项卡$ scope对象上定义了addPane(),则窗格指令将无法访问此函数,因此它无法与选项卡$ scope通信。)

要回答问题的其他部分:how does $scope work in controllers?

在$ scope上定义的函数中,this设置为“调用函数时/当有效的$ scope”。假设我们有以下HTML:

<div ng-controller="ParentCtrl">
   <a ng-click="logThisAndScope()">log "this" and $scope</a> - parent scope
   <div ng-controller="ChildCtrl">
      <a ng-click="logThisAndScope()">log "this" and $scope</a> - child scope
   </div>
</div>

ParentCtrl(Solely)有

$scope.logThisAndScope = function() {
    console.log(this, $scope)
}

点击第一个链接会显示this$scope相同,因为“调用该函数时生效的范围”是与之关联的范围ParentCtrl

点击第二个链接会显示this$scope 相同,因为“调用该函数时生效的范围 “是与ChildCtrl相关联的范围。因此,this设置为ChildCtrl的{​​{1}}。在方法内部,$scope仍然是$scope的$ scope。

Fiddle

我尝试不在$ scope中定义的函数中使用ParentCtrl,因为它会影响哪个$ scope受到影响,特别是考虑到ng-repeat,ng-include,ng-switch和指令都可以创建自己的子范围。

答案 1 :(得分:55)

分配'addPane'的原因是因为<pane>指令。

pane指令执行require: '^tabs',它将来自父指令的tabs控制器对象放入链接函数中。

addPane已分配给this,以便pane链接功能可以看到它。然后在pane链接函数中,addPane只是tabs控制器的一个属性,它只是tabsControllerObject.addPane。因此,pane指令的链接功能可以访问tabs控制器对象,从而访问addPane方法。

我希望我的解释很清楚......这很难解释。

答案 2 :(得分:26)

我刚刚阅读了关于两者之间差异的一个非常有趣的解释,并且越来越倾向于将模型附加到控制器并且将控制器别名以将模型绑定到视图。 http://toddmotto.com/digging-into-angulars-controller-as-syntax/是文章。他没有提到它,但是在定义指令时,如果你需要在多个指令之间共享某些东西而不想要一个服务(有合理的情况下服务很麻烦),那么将数据附加到父指令& #39; s控制器。 $ scope服务提供了大量有用的东西,$ watch是最明显的,但是如果只需要将数据绑定到视图,那么使用普通控制器和&#39;控制器作为&#39;在模板中很好,可以说是可取的。

答案 3 :(得分:18)

我建议你阅读以下帖子: AngularJS: "Controller as" or "$scope"?

它很好地描述了使用“Controller as”在“$ scope”上公开变量的优点。

我知道你特别询问过方法而不是变量,但我认为最好坚持使用一种技术并与之保持一致。

所以我认为,由于帖子中讨论的变量问题,最好只使用“Controller as”技术并将其应用于方法。

答案 4 :(得分:15)

在本课程(https://www.codeschool.com/courses/shaping-up-with-angular-js)中,他们解释了如何使用&#34;这个&#34;和许多其他的东西。

如果你通过&#34;这个&#34;将方法添加到控制器方法,您必须在视图中使用控制器的名称&#34; dot&#34;你的财产或方法。

例如,在视图中使用您的控制器,您可能会有以下代码:

    <div data-ng-controller="YourController as aliasOfYourController">

       Your first pane is {{aliasOfYourController.panes[0]}}

    </div>

答案 5 :(得分:3)

  

早期版本的Angular(1.0 RC之前版本)允许您使用它   可以与$ scope方法互换,但这不再是   案件。在范围和$ scope范围内定义的方法内部   可互换(角度设置为$ scope),但不是   在你的控制器构造函数中。

要恢复这种行为(有人知道它为什么会改变吗?)你可以添加:

return angular.extend($scope, this);

在控制器功能结束时(前提是$ scope已注入此控制器功能)。

这样可以通过控制器对象访问父作用域,这可以通过require: '^myParentDirective'

进入子对象

答案 6 :(得分:0)

$ scope有一个不同的'this'然后是控制器'this'。因此,如果你把一个console.log(这个)放在控制器内,它给你一个对象(控制器),this.addPane()将addPane方法添加到控制器对象。但$ scope具有不同的范围,其范围内的所有方法都需要由$ scope.methodName()加入。 控制器内部的this.methodName()表示在控制器对象中添加方法。$scope.functionName()是HTML和里面的

$scope.functionName(){
    this.name="Name";
    //or
    $scope.myname="myname"//are same}

将此代码粘贴到编辑器中并打开控制台以查看...

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>this $sope vs controller</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
    <script>
        var app=angular.module("myApp",[]);
app.controller("ctrlExample",function($scope){
          console.log("ctrl 'this'",this);
          //this(object) of controller different then $scope
          $scope.firstName="Andy";
          $scope.lastName="Bot";
          this.nickName="ABot";
          this.controllerMethod=function(){

            console.log("controllerMethod ",this);
          }
          $scope.show=function(){
              console.log("$scope 'this",this);
              //this of $scope
              $scope.message="Welcome User";
          }

        });
</script>
</head>
<body ng-app="myApp" >
<div ng-controller="ctrlExample">
       Comming From $SCOPE :{{firstName}}
       <br><br>
       Comming from $SCOPE:{{lastName}}
       <br><br>
       Should Come From Controller:{{nickName}}
       <p>
            Blank nickName is because nickName is attached to 
           'this' of controller.
       </p>

       <br><br>
       <button ng-click="controllerMethod()">Controller Method</button>

       <br><br>
       <button ng-click="show()">Show</button>
       <p>{{message}}</p>

   </div>

</body>
</html>