Angularjs: '構文としてのコントローラー'と$ watch


153

controller as構文を使用するときにプロパティの変更をサブスクライブするにはどうすればよいですか?

controller('TestCtrl', function ($scope) {
  this.name = 'Max';
  this.changeName = function () {
    this.name = new Date();
  }
  // not working       
  $scope.$watch("name",function(value){
    console.log(value)
  });
});
<div ng-controller="TestCtrl as test">
  <input type="text" ng-model="test.name" />
  <a ng-click="test.changeName()" href="#">Change Name</a>
</div>  

これはどうですか?$ watch()?有効です:this。$ watch( 'name'、...)
Joao Polo

回答:


160

関連するコンテキストをバインドするだけです。

$scope.$watch(angular.bind(this, function () {
  return this.name;
}), function (newVal) {
  console.log('Name changed to ' + newVal);
});

例:http : //jsbin.com/yinadoce/1/edit

更新:

Bogdan Gersakの答えは実際には同等であり、どちらの答えthisも適切なコンテキストにバインドしようとします。しかし、私は彼の答えがすっきりしていた。

そうは言っても、何よりもまず、その背後にある基本的な考え方を理解する必要があります。

更新2:

ES6を使用する人は、使用arrow functionすることで、適切なコンテキストOOTBの関数を取得できます。

$scope.$watch(() => this.name, function (newVal) {
  console.log('Name changed to ' + newVal);
});


9
これと$ scopeの混在を避けるために$ scopeなしで使用できますか?
Miron

4
いいえ、わかりましたが、まったく問題ありません。$scopeあなたのためにこれらの種類のメソッドを提供する一種のサービスです。
ロイ・ミロ2014

ここで、コントローラの名前とプロパティ " "のどちらnamereturn this.name;参照しているかを明確にできますかname
Jannik Jochem 2014

3
@Jannik、angular.bind限定されたコンテキスト(引数#1)を持つ関数を返します。このthis例では、コントローラーのインスタンスであるを関数(arg#2)にバインドするため、コントローラーのインスタンスのthis.nameプロパティnameを意味します。
ロイ・ミロ2014年

私はこれがどのように機能するかを理解したと思います。バインドされた関数が呼び出されると、監視された値に評価されるだけですよね?
Jannik Jochem 2014

138

私は通常これを行います:

controller('TestCtrl', function ($scope) {
    var self = this;

    this.name = 'Max';
    this.changeName = function () {
        this.name = new Date();
   }

   $scope.$watch(function () {
       return self.name;
   },function(value){
        console.log(value)
   });
});

3
これが最良の答えであることに同意しますが、これについての混乱は、おそらく最初の引数として関数を渡し、$scope.$watchその関数を使用してクロージャーから値を返すことにあると付け加えます。私はこれの別の例にまだ出くわしていませんが、それは機能し、最高です。以下の答え(つまり$scope.$watch('test.name', function (value) {});)を選択しなかったのは、テンプレートまたはui.routerの$ stateProviderにコントローラーの名前をハードコーディングする必要があり、そこで変更を加えると、うっかりウォッチャーが壊れてしまうためです。
Morris Singer

また、この回答と現在受け入れられている回答(を使用するangular.bind)の間の唯一の実質的な違いは、バインドするthisか、単にthisクロージャ内に別の参照を追加するかだけです。これらは機能的に同等であり、私の経験では、この種の選択は主観的な呼びかけであり、非常に強い意見の問題です。
Morris Singer

1
ES6の優れた点の1つは、適切なjsスコープを取得するために前述の2つの回避策を実行する必要がないことです。 $scope.$watch( ()=> { return this.name' }, function(){} ) 救助への太った矢
ジュソピ

1
また、ただ行うことができます() => this.name
coblr

これを動作させて$scope.$watchCollection、まだoldVal, newValパラメータを取得できますか?
クラーケン

23

以下を使用できます。

   $scope.$watch("test.name",function(value){
        console.log(value)
   });

これはあなたの例でJSFiddle動かしています。


25
このアプローチの問題は、JSがHTMLに依存するようになり、$ watchが機能するためにコントローラーをどこでも同じ名前(この場合は「test」)としてバインドすることを強制することです。微妙なバグを導入するのは非常に簡単でしょう。
jsdw 14

これは、すべてがディレクティブであるAngular 2のようなAngular 1を書いている場合にうまく機能することがわかります。Object.observeは今でもすばらしいでしょう。
ラングドン、2015年

13

「TestCtrl as test」から「test」を使用するのと同様に、別の回答で説明されているように、「自己」にスコープを割り当てることができます。

controller('TestCtrl', function($scope){
    var self = this;
    $scope.self = self;

    self.name = 'max';
    self.changeName = function(){
            self.name = new Date();
        }

    $scope.$watch("self.name",function(value){
            console.log(value)
        });
})

このようにして、DOMで指定された名前(「TestCtrl as test」)に縛られることはなく、関数に.bind(this)する必要もありません。

...元のhtmlを指定して使用する場合:

<div ng-controller="TestCtrl as test">
    <input type="text" ng-model="test.name" />
    <a ng-click="test.changeName()" href="#">Change Name</a>
</div>

1つのこと、つまり$scopeサービスについて知りたいだけな ので$scope.self = this、を追加した場合、別のコントローラで同じことをすると、そこで何が起きますか?
Vivek Kumar

12

AngularJs 1.5は、ControllerAs構造のデフォルトの$ ctrlをサポートしています。

$scope.$watch("$ctrl.name", (value) => {
    console.log(value)
});

$ watchGroupを使用すると機能しません。これは既知の制限ですか?この機能について何も見つからないので、この機能へのリンクを共有できますか。
user1852503

@ user1852503 docs.angularjs.org/guide/component比較表のディレクティブ/コンポーネントの定義を参照し、「controllerAs」レコードを確認してください。
Niels Steenbeek、2016年

今、私は分かる。あなたの答えは少し誤解を招くものです。識別子$ ctrlは、機能としてコントローラーと相関しません($ indexはng-repeatの例のように)、たまたまコンポーネント内のコントローラーのデフォルト名です(そして、質問は成分)。
user1852503 2016年

@ user1852503 1)$ ctrlはコントローラー(Controller as)を関連付けます。2)質問はコンポーネントについてです: "<div ng-controller =" TestCtrl as test ">"。3)このページのすべての回答は、どういうわけか私の答えと同じです。4)ドキュメントについては、$ watchGroupは$ watchに基づいているため、$ ctrl.nameを使用する場合に問題なく機能するはずです。
Niels Steenbeek、2016年

2

$ watch()の最初の引数として実際に関数を渡すことができます。

 app.controller('TestCtrl', function ($scope) {
 this.name = 'Max';

// hmmm, a function
 $scope.$watch(function () {}, function (value){ console.log(value) });
 });

つまり、this.name参照を返すことができます。

app.controller('TestCtrl', function ($scope) {
    this.name = 'Max';

    // boom
    $scope.$watch(angular.bind(this, function () {
    return this.name; // `this` IS the `this` above!!
    }), function (value) {
      console.log(value);
    });
});

controllerAsトピックに関する興味深い投稿を読むhttps://toddmotto.com/digging-into-angulars-controller-as-syntax/



0

ES6構文で$ watchを書くのは、思ったほど簡単ではありませんでした。できることは次のとおりです。

// Assuming
// controllerAs: "ctrl"
// or
// ng-controller="MyCtrl as ctrl"
export class MyCtrl {
  constructor ($scope) {
    'ngInject';
    this.foo = 10;
    // Option 1
    $scope.$watch('ctrl.foo', this.watchChanges());
    // Option 2
    $scope.$watch(() => this.foo, this.watchChanges());
  }

  watchChanges() {
    return (newValue, oldValue) => {
      console.log('new', newValue);
    }
  }
}

-1

:ViewとControllerがルートで、またはディレクティブ定義オブジェクトを介して結合されている場合、これは機能しません。以下に示すものは、HTMLに「SomeController as SomeCtrl」がある場合にのみ機能します。Mark Vが下のコメントで指摘しているように、彼が言うように、Bogdanがするようにした方が良いです。

私が使用するのvar vm = this;は、コントローラの最初で「これ」という言葉を邪魔にならないようにするためです。その後vm.name = 'Max';、私は時計でreturn vm.name。@Bogdanが「自己」を使用するのと同じように、「vm」を使用します。この変数は、「this」という単語が関数内で異なるコンテキストをとるため、「vm」または「self」が必要です。(したがって、this.nameを返すことはできません)そして、はい、$ watchに到達するには、$ scopeを美しい "controller as"ソリューションに挿入する必要があります。John Papaのスタイルガイドを参照してください:https : //github.com/johnpapa/angularjs-styleguide#controllers

function SomeController($scope, $log) {
    var vm = this;
    vm.name = 'Max';

    $scope.$watch('vm.name', function(current, original) {
        $log.info('vm.name was %s', original);
        $log.info('vm.name is now %s', current);
    });
}

11
これは、HTMLに「SomeController as vm」が含まれている限り機能します。ただし、誤解を招く可能性があります。監視式の「vm.name」は「var vm = this;」とは関係ありません。Bogdanが上で示したように、「controller as」で$ watchを使用する唯一の安全な方法は、最初の引数として関数を渡すことです。
Mark Visser

-1

$ scope(および$ watch!)なしでこれを行う方法は次のとおりです。

"controller as"構文を使用している場合は、$ scopeの使用を回避する方が適切でわかりやすくなります。

これがJSFiddleのコードです。(名前を保持するためにサービスを使用しています。それ以外の場合、ES5 Object.definePropertyのsetメソッドとgetメソッドが無限呼び出しを引き起こします。

var app = angular.module('my-module', []);

app.factory('testService', function() {
    var name = 'Max';

    var getName = function() {
        return name;
    }

    var setName = function(val) {
        name = val;
    }

    return {getName:getName, setName:setName};
});

app.controller('TestCtrl', function (testService) {
    var vm = this;

    vm.changeName = function () {
        vm.name = new Date();
    }

    Object.defineProperty(this, "name", {
        enumerable: true,
        configurable: false,
        get: function() {
            return testService.getName();
        },
        set: function (val) {
            testService.setName(val);
            console.log(vm.name);
        }
    }); 
});

フィドルは機能せず、これはオブジェクトプロパティを監視しません。
Rootical V.

@RooticalV。バイオリンが効いている。(AngualrJSを実行しているときは、ロードタイプを
nowrap

申し訳ありませんが、それでも実行できませんでした。ソリューションが非常に不利なので、残念です
happyZZR1400

@happyライブラリをAngular 1.4として選択してください。(2.0が機能するかどうかはわかりません)で、ロードタイプを[折り返しなし]にして、[実行]をクリックします。うまくいくはずです。
Binu Jasim 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.