web-dev-qa-db-ja.com

AngularJSコントローラの 'this'と$スコープ

AngularJSのホームページの「コンポーネントの作成」セクション に、この例があります。

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)。ただし、それ以外はコントローラーコンストラクターの内部では交換できません。

AngularJSコントローラでthis$scopeはどのように機能しますか?

998
Alexei Boronine

thisおよび$scopeは、AngularJSコントローラーでどのように機能しますか?」

短い答え

  • this
    • コントローラーコンストラクター関数が呼び出されると、thisがコントローラーになります。
    • $scopeオブジェクトで定義された関数が呼び出されると、thisは「関数が呼び出されたときに有効なスコープ」になります。これは、関数が定義されている$scopeである場合とそうでない場合があります。そのため、関数内では、this$scopeは同じnotになる場合があります。
  • $scope
    • すべてのコントローラーには、$scopeオブジェクトが関連付けられています。
    • コントローラー(コンストラクター)関数は、関連する$scopeでモデルのプロパティと関数/動作を設定します。
    • この$scopeオブジェクト(およびプロトタイプの継承が有効な場合は親スコープオブジェクト)で定義されたメソッドのみがHTML /ビューからアクセス可能です。例:ng-click、フィルターなどから.

長答

コントローラー関数はJavaScriptコンストラクター関数です。コンストラクター関数が実行されると(たとえば、ビューが読み込まれると)、this(つまり、「関数コンテキスト」)がコントローラーオブジェクトに設定されます。そのため、「tabs」コントローラーコンストラクター関数では、addPane関数が作成されます。

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

$ scopeではなく、コントローラーオブジェクトで作成されます。ビューはaddPane関数を見ることができません。$ scopeで定義された関数にのみアクセスできます。言い換えると、HTMLでは、これは機能しません。

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

「タブ」コントローラーコンストラクター関数が実行されると、次のようになります。

after tabs controller constructor function

黒い破線はプロトタイプの継承を示します-分離スコープは Scope からプロトタイプ的に継承します。 (HTMLでディレクティブが検出された有効なスコープからプロトタイプ的に継承することはありません。)

現在、ペインディレクティブのリンク関数は、tabsディレクティブと通信する必要があります(つまり、何らかの方法でタブアイソレート$ scopeに影響を与える必要があるということです)。イベントを使用できますが、別のメカニズムは、ペインディレクティブrequireをタブコントローラーにすることです。 (タブ変数$ _scopeのrequireへのペインディレクティブのメカニズムはないようです。)

そこで、これは疑問を投げかけます。タブコントローラーにのみアクセスできる場合、どのようにしてタブアイソレート$ scopeにアクセスできますか(これが本当に必要なことです)。

さて、赤い点線が答えです。 addPane()関数の「スコープ」(ここではJavaScriptの関数スコープ/クロージャーを参照しています)により、関数はタブの$ scopeを分離することができます。つまり、addPane()が定義されたときにクロージャーが作成されたため、addPane()は上の図の「タブIsolateScope」にアクセスできます。 (代わりにtabs $ scopeオブジェクトでaddPane()を定義した場合、ペインディレクティブはこの関数にアクセスできないため、tabs $ 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(唯一)は

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

最初のリンクをクリックすると、this$scopeが同じであることが表示されます。これは、「関数が呼び出されたときに有効なスコープ」がParentCtrlに関連付けられたスコープだからです。

2番目のリンクをクリックすると、this$scopenot同じであることがわかります。これは、「関数が呼び出されました "はChildCtrlに関連付けられたスコープです。したがって、ここでは、thisChildCtrl$scopeに設定されます。メソッド内では、$scopeは依然としてParentCtrlの$ scopeです。

フィドル

特に、ng-repeat、ng-include、ng-switch、およびディレクティブがすべて独自の子スコープを作成できることを考慮すると、どの$ scopeが影響を受けているかがわかりにくくなるため、$ scopeで定義された関数内でthisを使用しないようにしています。

985
Mark Rajcok

これに 'addPane'が割り当てられているのは、<pane>ディレクティブが原因です。

paneディレクティブはrequire: '^tabs'を実行します。これは、タブディレクティブオブジェクトを親ディレクティブからリンク関数に渡します。

addPanethisに割り当てられているので、paneリンク関数はそれを見ることができます。それでpaneリンク関数では、addPanetabsコントローラーの単なるプロパティーであり、それはtabsControllerObject.addPaneです。そのため、paneディレクティブのリンク関数はtabsコントローラオブジェクトにアクセスできるため、addPaneメソッドにアクセスできます。

私の説明が十分明確であることを願っています。説明するのは難しいです。

54
Andrew Joslin

この2つの違いについてのかなり興味深い説明と、モデルをコントローラにアタッチしたり、モデルをビューにバインドするためにコントローラをエイリアスにしたりする傾向が高まっています。 http://toddmotto.com/digging-into-angulars-controller-as-syntax/ が記事です。彼はそれについては触れていませんが、ディレクティブを定義するときに、複数のディレクティブ間で何かを共有する必要があり、サービスが欲しくない場合(サービスが面倒な合法的な場合があります)、データを親ディレクティブのコントローラーに添付します。 $ scopeサービスはたくさんの便利なものを提供します。$ watchが最も明白ですが、データをビューにバインドする必要がある場合は、プレーンコントローラとテンプレートの 'controller as'を使用するのが賢く、間違いなく好ましいです。

26
Derek

次の記事を読むことをお勧めします: AngularJS: "Controller as"または "$ scope"?

これは、 "$ scope"を超えて変数を公開するために "Controller as"を使用することの利点を非常によく説明しています。

私はあなたが変数ではなくメソッドについて具体的に尋ねたことを知っています、しかし私はそれが一つのテクニックに固執してそれと一致しているほうが良いと思います。

それで、私の意見では、この記事で議論した変数の問題のために、単に "Controller as"テクニックを使い、それをメソッドにも適用するのが良いでしょう。

18
Liran Brimer

このコース( https://www.codeschool.com/courses/shaping-up-with-angular-js )では、「これ」や他の多くのものの使い方を説明しています。

"this"メソッドを使ってコントローラにメソッドを追加した場合は、プロパティまたはメソッドの "dot"というコントローラ名でそれをビュー内で呼び出す必要があります。

たとえば、ビューでコントローラを使用すると、次のようなコードになります。

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

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

    </div>
15
Sandro

以前のバージョンのAngular(1.0 RC以前)では、これを$ scopeメソッドと互換的に使用できましたが、これはもはや当てはまりません。スコープthisと$ scopeで定義されたメソッドの中では互換性があります(angleはこれを$ scopeに設定します)が、そうでなければあなたのコントローラコンストラクタの中ではそうではありません。

この振る舞いを取り戻すために(誰がそれがなぜ変更されたのか知っていますか?)あなたは追加することができます:

return angular.extend($scope, this);

あなたのコントローラ関数の最後に($ scopeがこのコントローラ関数に挿入されていれば).

これは、require: '^myParentDirective'を使って子に取得できるコントローラオブジェクトを介して親スコープにアクセスできるという素晴らしい効果があります。

3
Kamil Szot

$ scopeにはコントローラー「this」とは異なる「this」があります。したがって、console.log(this)をコントローラー内に配置すると、オブジェクト(controller)が提供され、this.addPane()がコントローラーオブジェクトにaddPaneメソッドを追加します。ただし、$ scopeのスコープは異なり、そのスコープ内のすべてのメソッドは$ scope.methodName()にアクセスする必要があります。 this.methodName()コントローラー内部では、コントローラーオブジェクト内にmethosを追加することを意味します。$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>
0
Aniket Jha