カスタムディレクティブを新しい コンポーネントアーキテクチャ にアップグレードしています。コンポーネントはウォッチャーをサポートしていません。これは正しいです?その場合、オブジェクトの変更をどのように検出しますか?基本的な例として、ゲームにバインドされた子コンポーネントゲームを持つカスタムコンポーネント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-->
この回答では、 watchers。 を使用せずにAngularJS 1.5コンポーネントを作成するために使用する5つのテクニックの概要を説明します
ng-change
ディレクティブ を使用します$onChanges
Life-cycle Hook を使用します$doCheck
Life-cycle Hook を使用します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);
});
これは今後推奨される方法です。 $watch
を使用するAngularJS戦略は、ポーリング戦略であるためスケーラブルではありません。 $watch
リスナーの数が2000年頃になると、UIが遅くなります。 Angular 2の戦略は、フレームワークをより反応的にし、$watch
を$scope
に配置しないようにすることです。
$onChanges
ライフサイクルフックを使用するバージョン1.5.3では、AngularJSは$onChanges
ライフサイクルフックを$compile
サービスに追加しました。
ドキュメントから:
コントローラーは、ライフサイクルフックとして機能する次のメソッドを提供できます。
- $ onChanges(changesObj)-一方向(
<
)または補間(@
)バインディングが更新されるたびに呼び出されます。changesObj
は、キーが変更されたバインドされたプロパティの名前であるハッシュであり、値は{ currentValue: ..., previousValue: ... }
という形式のオブジェクトです。このフックを使用して、バインドされた値のクローンを作成するなど、コンポーネント内で更新をトリガーし、外部値の偶発的な変更を防ぎます。
$onChanges
フックは、<
一方向バインディングを使用して、コンポーネントへの外部変更に対応するために使用されます。 ng-change
ディレクティブは、ng-model
バインディングを使用して、コンポーネントの外部の&
コントローラーからの変更を伝達するために使用されます。
$doCheck
ライフサイクルフックを使用するversion 1.5.8で、AngularJSは$doCheck
ライフサイクルフックを$compile
サービスに追加しました。
ドキュメントから:
コントローラーは、ライフサイクルフックとして機能する次のメソッドを提供できます。
$doCheck()
-ダイジェストサイクルの各ターンで呼び出されます。変更を検出して対処する機会を提供します。検出した変更に応じて実行するアクションは、このフックから呼び出す必要があります。これを実装しても、$onChanges
が呼び出されるタイミングには影響しません。たとえば、このフックは、Angularの変更ディテクターによって検出されない変更を$onChanges
でトリガーしない、深い等価チェックを実行する場合、またはDateオブジェクトをチェックする場合に役立ちます。このフックは引数なしで呼び出されます。変更を検出する場合、現在の値と比較するために以前の値を保存する必要があります。
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 Extensions for Angular でサービスを構築します。
<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のデモ 。
$watch
オブジェクトは$scope
オブジェクト内で使用できるため、コントローラーファクトリ関数内に$scope
を追加してから、変数にウォッチャーを配置する必要があります。
$scope.$watch(function(){ return myBox.game; }, function(newVal){ alert('Value changed to '+ newVal) });
注:$scope
の依存関係を削除して、Angular2に一歩近づくためにdirective
をcomponent
に変換したことを知っています。しかし、このケースでは削除されなかったようです。
更新
基本的にangular 1.5は.component
メソッドを追加して、2つの異なる機能を区別しています。 component
。standsと同様に、selector
を追加して特定の動作を実行します。ここで、directive
はDOMに特定の動作を追加することを意味します。ディレクティブは、.directive
DDO(ディレクティブ定義オブジェクト)の単なるラッパーメソッドです。表示されるのは、angularコンパイル済みDOMを取得できるlink/compile
メソッドを使用しているときに.component
関数を削除したことだけです。
Angularコンポーネントライフサイクルフックの$onChanges
/$doCheck
ライフサイクルフックを使用してください。これらはAngular 1.5.3+バージョン以降で使用可能になります。
$ onChanges(changesObj)-バインディングが更新されるたびに呼び出されます。 changesObjは、キーがバインドされたプロパティの名前であるハッシュです。
$ doCheck()-バインドの変更時にダイジェストサイクルの各ターンで呼び出されます。変更を検出して対処する機会を提供します。
コンポーネント内で同じ関数を使用することにより、コードがAngularに移行するための互換性を確保します2。
私のソリューションに興味のある人には、RXJS Observablesに頼ることになります。これは、Angularに到達するときに使用しなければならないものです。何を見ますか。
class BoxCtrl {
constructor(msgService) {
this.msgService = msgService
this.msg = ''
this.subscription = msgService.subscribe((obj) => {
console.log('Subscribed')
this.msg = obj
})
}
unsubscribe() {
console.log('Unsubscribed')
msgService.usubscribe(this.subscription)
}
}
var app = angular
.module('app', ['ngMaterial'])
.controller('MainCtrl', ($scope, msgService) => {
$scope.name = "Observer App Example";
$scope.msg = 'Message';
$scope.broadcast = function() {
msgService.broadcast($scope.msg);
}
})
.component("box", {
bindings: {},
controller: 'BoxCtrl',
template: `Listener: </br>
<strong>{{$ctrl.msg}}</strong></br>
<md-button ng-click='$ctrl.unsubscribe()' class='md-warn'>Unsubscribe A</md-button>`
})
.factory('msgService', ['$http', function($http) {
var subject$ = new Rx.ReplaySubject();
return {
subscribe: function(subscription) {
return subject$.subscribe(subscription);
},
usubscribe: function(subscription) {
subscription.dispose();
},
broadcast: function(msg) {
console.log('success');
subject$.onNext(msg);
}
}
}])
angular 1.5コンポーネントとともに、受け入れられた回答で推奨されているng-change
の使用に関する小さなヘッズアップ。
ng-model
およびng-change
が機能しないコンポーネントを監視する必要がある場合は、次のようにパラメーターを渡すことができます。
コンポーネントが使用されるマークアップ:
<my-component on-change="$ctrl.doSth()"
field-value="$ctrl.valueToWatch">
</my-component>
コンポーネントjs:
angular
.module('myComponent')
.component('myComponent', {
bindings: {
onChange: '&',
fieldValue: '='
}
});
コンポーネントのマークアップ:
<select ng-model="$ctrl.fieldValue"
ng-change="$ctrl.onChange()">
</select>
遅刻だ。しかし、それは他の人々に役立ちます。
app.component("headerComponent", {
templateUrl: "templates/header/view.html",
controller: ["$rootScope", function ($rootScope) {
let $ctrl = this;
$rootScope.$watch(() => {
return $ctrl.val;
}, function (newVal, oldVal) {
// do something
});
}]
});
IE11で利用可能なMutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver 。 DOM /コントローラーの分離を半壊するコントローラーに$ elementサービスを注入する必要がありますが、これは、angularjsの基本的な例外(つまり、欠陥)であると感じています。 hide/showは非同期であるため、angularjsとangular-bootstrap-tabが提供しないon-showコールバックが必要です。また、どの特定のDOM要素uが観察したいかを知る必要があります。 Anglejsコントローラーの次のコードを使用して、ショーでHighchartsチャートリフローをトリガーしました。
const myObserver = new MutationObserver(function (mutations) {
const isVisible = $element.is(':visible') // Requires jquery
if (!_.isEqual(isVisible, $element._prevIsVisible)) { // Lodash
if (isVisible) {
$scope.$broadcast('onReflowChart')
}
$element._prevIsVisible = isVisible
}
})
myObserver.observe($element[0], {
attributes: true,
attributeFilter: ['class']
})
本当にナイスな回答を受け入れましたが、イベントのパワーも使用できると付け加えるかもしれません(もしそうなら、Qtシグナル/スロットのように)。
イベントはブロードキャストされます:$rootScope.$broadcast("clickRow", rowId)
は、親(または子コントローラー)によっても行われます。次に、コントローラーで次のようにイベントを処理できます。
$scope.$on("clickRow", function(event, data){
// do a refresh of the view with data == rowId
});
このようなログを追加することもできます(ここから取得: https://stackoverflow.com/a/34903433/3147071 )
var withLogEvent = true; // set to false to avoid events logs
app.config(function($provide) {
if (withLogEvent)
{
$provide.decorator("$rootScope", function($delegate) {
var Scope = $delegate.constructor;
var origBroadcast = Scope.prototype.$broadcast;
var origEmit = Scope.prototype.$emit;
Scope.prototype.$broadcast = function() {
console.log("$broadcast was called on $scope " + this.$id + " with arguments:",
arguments);
return origBroadcast.apply(this, arguments);
};
Scope.prototype.$emit = function() {
console.log("$emit was called on $scope " + this.$id + " with arguments:",
arguments);
return origEmit.apply(this, arguments);
};
return $delegate;
});
}
});