Komponenten ohne Beobachter schreiben
In dieser Antwort werden fünf Techniken beschrieben, mit denen AngularJS 1.5-Komponenten ohne Verwendung von Watchern geschrieben werden können.
Verwenden Sie die ng-change
Richtlinie
Welche alternativen Methoden stehen zur Verfügung, um Änderungen des Objektzustands zu beobachten, ohne watch zur Vorbereitung auf AngularJs2 zu verwenden?
Mit der ng-change
Direktive können Sie auf Eingabeänderungen reagieren.
<textarea ng-model='game.game'
ng-change="game.textChange(game.game)">
</textarea>
Um das Ereignis an eine übergeordnete Komponente weiterzugeben, muss der Ereignishandler als Attribut der untergeordneten Komponente hinzugefügt werden.
<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"
});
Und in der übergeordneten Komponente:
myBox.gameChange = function(newValue) {
console.log(newValue);
});
Dies ist die bevorzugte Methode für die Zukunft. Die AngularJS-Strategie $watch
ist nicht skalierbar, da es sich um eine Abfragestrategie handelt. Wenn die Anzahl der $watch
Hörer etwa 2000 erreicht, wird die Benutzeroberfläche träge. Die Strategie in Angular 2 besteht darin, das Framework reaktiver zu gestalten und das Anbringen $watch
zu vermeiden $scope
.
Verwenden Sie den $onChanges
Lebenszyklus-Haken
Mit Version 1.5.3 hat AngularJS $onChanges
dem $compile
Service den Life-Cycle-Hook hinzugefügt .
Aus den Dokumenten:
Die Steuerung kann die folgenden Methoden bereitstellen, die als Lebenszyklus-Hooks fungieren:
- $ onChanges (changesObj) - Wird aufgerufen, wenn Einweg- (
<
) oder Interpolationsbindungen ( @
) aktualisiert werden. Dies changesObj
ist ein Hash, dessen Schlüssel die Namen der gebundenen Eigenschaften sind, die sich geändert haben, und deren Werte ein Objekt des Formulars sind { currentValue: ..., previousValue: ... }
. Verwenden Sie diesen Hook, um Aktualisierungen innerhalb einer Komponente auszulösen, z. B. das Klonen des gebundenen Werts, um eine versehentliche Mutation des äußeren Werts zu verhindern.
- AngularJS Comprehensive Directive API-Referenz - Life-Cycle-Hooks
Der $onChanges
Haken wird verwendet, um auf externe Änderungen an der Komponente mit <
Einwegbindungen zu reagieren . Die ng-change
Direktive wird verwendet, um Änderungen von der ng-model
Steuerung außerhalb der Komponente mit &
Bindungen zu propagieren .
Verwenden Sie den $doCheck
Lebenszyklus-Haken
Mit Version 1.5.8 hat AngularJS $doCheck
dem $compile
Service den Life-Cycle-Hook hinzugefügt .
Aus den Dokumenten:
Die Steuerung kann die folgenden Methoden bereitstellen, die als Lebenszyklus-Hooks fungieren:
$doCheck()
- Wird bei jeder Umdrehung des Verdauungszyklus aufgerufen. Bietet die Möglichkeit, Änderungen zu erkennen und darauf zu reagieren. Alle Aktionen, die Sie als Reaktion auf die von Ihnen erkannten Änderungen ausführen möchten, müssen über diesen Hook aufgerufen werden. Die Implementierung hat keine Auswirkung darauf, wann $onChanges
aufgerufen wird. Dieser Hook kann beispielsweise nützlich sein, wenn Sie eine gründliche Gleichheitsprüfung durchführen oder ein Datumsobjekt prüfen möchten, dessen Änderungen vom Änderungsdetektor von Angular nicht erkannt und somit nicht ausgelöst werden $onChanges
. Dieser Hook wird ohne Argumente aufgerufen. Wenn Sie Änderungen erkennen, müssen Sie die vorherigen Werte zum Vergleich mit den aktuellen Werten speichern.
- AngularJS Comprehensive Directive API-Referenz - Life-Cycle-Hooks
Intercomponent Kommunikation mit require
Direktiven können erfordern, dass die Controller anderer Direktiven die Kommunikation untereinander ermöglichen. Dies kann in einer Komponente erreicht werden, indem eine Objektzuordnung für die Eigenschaft require bereitgestellt wird . Die Objektschlüssel geben die Eigenschaftsnamen an, unter denen die erforderlichen Controller (Objektwerte) an den Controller der erforderlichen Komponente gebunden werden.
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'
});
Weitere Informationen finden Sie im AngularJS Developer Guide - Intercomponent Communicatation
Push-Werte von einem Service mit RxJS
Was ist mit einer Situation, in der Sie beispielsweise einen Dienst haben, der den Status "Halten" hat? Wie kann ich Änderungen an diesem Service vornehmen, und andere zufällige Komponenten auf der Seite können sich einer solchen Änderung bewusst sein? Ich hatte in letzter Zeit Probleme, dieses Problem anzugehen
Erstellen Sie einen Dienst mit RxJS-Erweiterungen für 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);
}
};
});
Dann abonnieren Sie einfach die Änderungen.
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();
};
});
Kunden können Änderungen mit abonnieren DataService.subscribe
und Produzenten können Änderungen mit vorantreiben DataService.set
.
Die DEMO auf PLNKR .
myBox.game
Variablen umgehen ?Service
dass der Staat zum Beispiel zu halten. Wie kann ich Änderungen an diesem Service vornehmen, und andere zufällige Komponenten auf der Seite können sich einer solchen Änderung bewusst sein?$watch
Das Objekt ist innerhalb des$scope
Objekts verfügbar. Sie müssen also$scope
die Factory-Funktion Ihres Controllers hinzufügen und dann den Watcher über der Variablen platzieren.$scope.$watch(function(){ return myBox.game; }, function(newVal){ alert('Value changed to '+ newVal) });
Demo hier
Aktualisieren
Grundsätzlich unterscheidet Winkel 1.5 die hinzugefügte
.component
Methode, um zwei verschiedene Funktionen zu unterscheiden. Wiecomponent
.stands, um ein bestimmtes Verhalten hinzuzufügenselector
, wobei asdirective
steht, um DOM ein bestimmtes Verhalten hinzuzufügen. Die Direktive ist nur eine Wrapper-Methode für.directive
DDO (Directive Definition Object). Sie können nur sehen, dass sie eine Entfernungsfunktion hatten,link/compile
während.component
Sie eine Methode verwendeten, bei der Sie die Möglichkeit hatten, ein eckig kompiliertes DOM zu erhalten.Verwenden Sie den
$onChanges
/$doCheck
Lifecycle-Hook des Angular-Komponenten-Lifecycle-Hooks. Diese sind ab der Angular 1.5.3+ -Version verfügbar.Durch die Verwendung derselben Funktion innerhalb der Komponente wird sichergestellt, dass Ihr Code kompatibel ist, um zu Angular 2 zu wechseln.
quelle
observable
vonRxJS
, das wäre gut kompatibel mitAngular2
Für alle, die an meiner Lösung interessiert sind, greife ich am Ende auf RXJS Observables zurück, die Sie verwenden müssen, wenn Sie zu Angular 2 gelangen. Hier ist eine funktionierende Geige für die Kommunikation zwischen Komponenten, mit der ich mehr Kontrolle darüber habe, was ich sehen soll.
JS FIDDLE RXJS Observables
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); } } }])
quelle
Ein kleines Heads-up zur Verwendung von
ng-change
, wie in der akzeptierten Antwort empfohlen, zusammen mit einer eckigen 1,5-Komponente.Im Fall , dass Sie eine Komponente , die beobachten
ng-model
undng-change
nicht arbeiten, können Sie die Parameter übergeben , wie:Markup, in dem die Komponente verwendet wird:
<my-component on-change="$ctrl.doSth()" field-value="$ctrl.valueToWatch"> </my-component>
Komponente js:
angular .module('myComponent') .component('myComponent', { bindings: { onChange: '&', fieldValue: '=' } });
Komponenten-Markup:
<select ng-model="$ctrl.fieldValue" ng-change="$ctrl.onChange()"> </select>
quelle
Verfügbar in IE11, MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver . Sie müssen $ element service in den Controller einfügen, wodurch die Trennung von DOM und Controller halb unterbrochen wird, aber ich bin der Meinung, dass dies eine grundlegende Ausnahme (dh ein Fehler) bei Angularjs ist. Da hide / show asynchron ist, benötigen wir einen On-Show-Rückruf, den anglejs & angular-bootstrap-tab nicht bieten. Es erfordert auch, dass Sie wissen, welches spezifische DOM-Element Sie beobachten möchten. Ich habe den folgenden Code für den AngularJS-Controller verwendet, um den angezeigten Highcharts-Diagramm-Reflow auszulösen.
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'] })
quelle
$scope
und, um die Migration zu Angular2 + zu vereinfachen$rootScope
. Verwenden Sie stattdessen RxJS für Ereignisemitter und Abonnenten.Wirklich nette akzeptierte Antwort, aber ich könnte hinzufügen, dass Sie auch die Kraft von Ereignissen nutzen können (ein bisschen wie bei Qt-Signalen / Slots, wenn Sie so wollen).
Ein Ereignis wird gesendet:
$rootScope.$broadcast("clickRow", rowId)
von einem übergeordneten (oder sogar untergeordneten Controller). Dann können Sie in Ihrem Controller das Ereignis wie folgt behandeln:$scope.$on("clickRow", function(event, data){ // do a refresh of the view with data == rowId });
Sie können auch eine solche Protokollierung hinzufügen (von hier aus: 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; }); } });
quelle
$scope
und$rootScope
. Verwenden Sie stattdessen RxJS für Ereignisemitter und Abonnenten.Ich bin spät dran. Aber es kann anderen Menschen helfen.
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 }); }] });
quelle