1. ホーム
  2. angularjs

AngularJS 1.5+ ComponentsはWatcherをサポートしませんが、どのように対処しますか?

2023-07-24 09:59:01

質問

カスタムディレクティブを新しい コンポーネントアーキテクチャにアップグレードしました。 . コンポーネントはウォッチャーをサポートしないと読みました。これは正しいのでしょうか?もしそうなら、どのようにオブジェクトの変更を検出するのでしょうか?基本的な例として、私はカスタムコンポーネント myBox これは、ゲーム上のバインディングを持つ子コンポーネントゲームを持っています。ゲームコンポーネント内の変更ゲームがある場合、どのように私はmyBox内のアラートメッセージを表示するには?私はrxJSのメソッドがあることを理解していますが、これを純粋にアンギュラーで行うことは可能ですか?私の JSFiddle

JavaScript

var app = angular.module('myApp', []);
app.controller('mainCtrl', function($scope) {

   $scope.name = "Tony Danza";

});

app.component("myBox",  {
      bindings: {},
      controller: function($element) {
        var myBox = this;
        myBox.game = 'World Of warcraft';
        //IF myBox.game changes, show alert message 'NAME CHANGE'
      },
      controllerAs: 'myBox',
      templateUrl: "/template",
      transclude: true
})
app.component("game",  {
      bindings: {game:'='},
      controller: function($element) {
        var game = this;


      },
      controllerAs: 'game',
      templateUrl: "/template2"
})

HTML

<div ng-app="myApp" ng-controller="mainCtrl">
  <script type="text/ng-template" id="/template">
    <div style='width:40%;border:2px solid black;background-color:yellow'>
      Your Favourite game is: {{myBox.game}}
      <game game='myBox.game'></game>
    </div>
  </script>

 <script type="text/ng-template" id="/template2">
    <div>
    </br>
        Change Game
      <textarea ng-model='game.game'></textarea>
    </div>
  </script>

  Hi {{name}}
  <my-box>

  </my-box>

</div><!--end app-->

どのように解決するのですか?

のないコンポーネントの書き方 ウォッチャ

この回答では、AngularJS 1.5のコンポーネントを書く際に ウォッチャーを使用せずに


を使用します。 ng-change ディレクティブ

AngularJs2の準備として、watchを使用せずにobjの状態変化を観察するために利用できるaltメソッドは何ですか?

を使うことができます。 ng-change ディレクティブを使って、入力の変化に反応することができます。

<textarea ng-model='game.game' 
          ng-change="game.textChange(game.game)">
</textarea>

また、イベントを親コンポーネントに伝搬させるには、子コンポーネントの属性としてイベントハンドラを追加する必要があります。

<game game='myBox.game' game-change='myBox.gameChange($value)'></game>

JS

app.component("game",  {
      bindings: {game:'=',
                 gameChange: '&'},
      controller: function() {
        var game = this;
        game.textChange = function (value) {
            game.gameChange({$value: value});
        });

      },
      controllerAs: 'game',
      templateUrl: "/template2"
});

そして、親コンポーネントでは

myBox.gameChange = function(newValue) {
    console.log(newValue);
});

これは今後望ましい方法です。AngularJSの戦略である $watch を使用する AngularJS の戦略は、ポーリング戦略であるため、スケーラブルではありません。なぜならポーリング戦略だからです。 $watch リスナーの数が約2000に達すると、UIが重くなります。Angular 2の戦略は、フレームワークをよりリアクティブにし、を配置しないようにすることです。 $watch の上に $scope .


を使用します。 $onChanges ライフサイクルフック

バージョン 1.5.3 で、AngularJS は $onChanges ライフサイクルフックを $compile サービスにフックします。

Docsから。

コントローラは、ライフサイクルフックとして動作する以下のメソッドを提供することができます。

  • $onChanges(changesObj) - 片方向の( < ) または補間 ( @ ) のバインディングが更新されます。そのため changesObj はハッシュで、キーは変更されたバインドプロパティの名前、 値は以下の形式のオブジェクトです。 { currentValue: ..., previousValue: ... } . このフックは、外側の値が誤って変更されるのを防ぐために、バインドされた値をクローンするなどのコンポーネント内の更新をトリガーするために使用します。

- AngularJS Comprehensive Directive API リファレンス -- ライフサイクルフック

この $onChanges フックは、コンポーネントへの外部からの変更に反応するために < 一方向バインディングで このフックは ng-change ディレクティブは ng-model ディレクティブを使用して、 コンポーネントの外側にある & バインディングを使用します。


を使用します。 $doCheck ライフサイクルフック

バージョン 1.5.8 で、AngularJS は $doCheck ライフサイクルフックを $compile サービスにフックします。

Docsから。

コントローラは、ライフサイクルフックとして動作する以下のメソッドを提供することができます。

  • $doCheck() - ダイジェストサイクルの各ターンで呼び出されます。変更を検出し、それに対応する機会を提供します。検出した変更に対応して取りたいアクションはすべてこのフックから呼び出されなければなりません; これを実装しても、いつ $onChanges が呼び出されるタイミングには影響しません。例えば、このフックは、深い等式検査を行いたい場合や、Angularの変更検出器では検出されないDateオブジェクトの変更をチェックしたい場合などに有用です。 $onChanges . このフックは引数なしで呼び出されます。変更を検出する場合、現在の値と比較するために以前の値を保存する必要があります。

- AngularJS Comprehensive Directive API リファレンス -- ライフサイクルフック


とのコンポーネント間通信 require

ディレクティブは を要求する ディレクティブは、他のディレクティブのコントローラを必要とし、 相互の通信を可能にします。これは、コンポーネント内で require プロパティにオブジェクトマッピングを提供することで実現できます。オブジェクトキーは、要求されたコントローラ (オブジェクトの値) が要求しているコンポーネントのコントローラにバインドされるプロパティ名を指定します。

app.component('myPane', {
  transclude: true,
  require: {
    tabsCtrl: '^myTabs'
  },
  bindings: {
    title: '@'
  },
  controller: function() {
    this.$onInit = function() {
      this.tabsCtrl.addPane(this);
      console.log(this);
    };
  },
  templateUrl: 'my-pane.html'
});

詳しくは AngularJS開発者ガイド - コンポーネント間通信


サービスから値をプッシュする RxJS

例えば、状態を保持しているサービスがある場合はどうでしょうか。そのサービスに変更を加え、ページ上の他のランダムなコンポーネントがそのような変更を認識できるようにするには、どうすればよいでしょうか。最近、この問題に取り組むのに苦労しています。

でサービスを構築します。 AngularのためのRxJS拡張機能 .

<script src="//unpkg.com/angular/angular.js"></script>
<script src="//unpkg.com/rx/dist/rx.all.js"></script>
<script src="//unpkg.com/rx-angular/dist/rx.angular.js"></script>

var app = angular.module('myApp', ['rx']);

app.factory("DataService", function(rx) {
  var subject = new rx.Subject(); 
  var data = "Initial";

  return {
      set: function set(d){
        data = d;
        subject.onNext(d);
      },
      get: function get() {
        return data;
      },
      subscribe: function (o) {
         return subject.subscribe(o);
      }
  };
});

そして、単純に変更を購読します。

app.controller('displayCtrl', function(DataService) {
  var $ctrl = this;

  $ctrl.data = DataService.get();
  var subscription = DataService.subscribe(function onNext(d) {
      $ctrl.data = d;
  });

  this.$onDestroy = function() {
      subscription.dispose();
  };
});

クライアントは変更点を DataService.subscribe で、プロデューサは DataService.set .

PLNKRのDEMO .