J'ai mis à niveau mes directives personnalisées vers la nouvelle architecture composant . J'ai lu que les composants ne prennent pas en charge les observateurs. Est-ce correct? Si oui, comment détectez-vous les changements sur un objet? Pour un exemple de base, j'ai un composant personnalisé myBox
qui a un jeu de composants enfant avec une liaison sur le jeu. S'il y a un jeu de modification dans le composant de jeu, comment puis-je afficher un message d'alerte dans la myBox? Je comprends qu’il existe une méthode rxJS est-il possible de le faire uniquement en angulaire? Mon 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-->
Cette réponse décrit cinq techniques à utiliser pour écrire des composants AngularJS 1.5 sans utiliser watchers.
ng-change
$onChanges
cycle de vie$doCheck
cycle de vieng-change
quelles méthodes alternatives sont disponibles pour observer les changements d'état d'obj sans utiliser watch en préparation de AngularJs2?
Vous pouvez utiliser la directive ng-change
pour réagir aux modifications d’entrée.
<textarea ng-model='game.game'
ng-change="game.textChange(game.game)">
</textarea>
Et pour propager l'événement à un composant parent, le gestionnaire d'événements doit être ajouté en tant qu'attribut du composant enfant.
<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"
});
Et dans le composant parent:
myBox.gameChange = function(newValue) {
console.log(newValue);
});
C'est la méthode privilégiée pour l'avenir. La stratégie d'AngularJS consistant à utiliser $watch
n'est pas évolutive, car il s'agit d'une stratégie d'interrogation. Lorsque le nombre d'auditeurs $watch
atteint environ 2 000, l'interface utilisateur devient lente. La stratégie dans Angular 2 est de rendre le framework plus réactif et d’éviter de placer $watch
sur $scope
.
$onChanges
cycle de vieAvec version 1.5.3, AngularJS a ajouté le hook $onChanges
cycle de vie au service $compile
.
De la documentation:
Le contrôleur peut fournir les méthodes suivantes qui agissent comme des crochets de cycle de vie:
- $ onChanges (changesObj) - Appelé chaque fois que des liaisons à sens unique (
<
) ou d'interpolation (@
) sont mises à jour. LachangesObj
est un hachage dont les clés sont les noms des propriétés liées qui ont changé et les valeurs sont un objet de la forme{ currentValue: ..., previousValue: ... }
. Utilisez ce crochet pour déclencher des mises à jour au sein d'un composant, telles que le clonage de la valeur liée afin d'empêcher toute mutation accidentelle de la valeur externe.- Référence API de la directive détaillée AngularJS - Crochets pour le cycle de vie
Le hook $onChanges
permet de réagir aux modifications externes apportées au composant avec des liaisons unidirectionnelles <
. La directive ng-change
permet de propager les modifications du contrôleur ng-model
en dehors du composant avec des liaisons &
.
$doCheck
cycle de vieAvec version 1.5.8, AngularJS a ajouté le hook $doCheck
cycle de vie au service $compile
.
De la documentation:
Le contrôleur peut fournir les méthodes suivantes qui agissent comme des crochets de cycle de vie:
$doCheck()
- Appelé à chaque tour du cycle de digestion. Fournit une opportunité de détecter et d'agir sur les changements. Toutes les actions que vous souhaitez effectuer en réponse aux modifications que vous détectez doivent être appelées à partir de ce point d'ancrage. l'implémentation n'a aucun effet sur le moment où$onChanges
est appelé. Par exemple, ce point d'ancrage peut être utile si vous souhaitez effectuer un contrôle d'égalité approfondi ou un objet Date, auquel le détecteur de changement d'Angular ne détectera aucune modification et ne déclenchera donc pas$onChanges
. Ce hook est invoqué sans arguments. si vous détectez des modifications, vous devez stocker la ou les valeurs précédentes pour les comparer aux valeurs actuelles.- Référence API de la directive détaillée AngularJS - Crochets pour le cycle de vie
require
Les directives peuvent requièrent les contrôleurs d’autres directives pour permettre la communication entre eux. Cela peut être réalisé dans un composant en fournissant un mappage d'objet pour la propriété require . Les clés d'objet spécifient les noms de propriété sous lesquels les contrôleurs requis (valeurs d'objet) seront liés au contrôleur du composant requis.
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'
});
Pour plus d'informations, voir Guide du développeur AngularJS - Communication entre composants
Qu'en est-il dans une situation où vous avez un service en attente, par exemple? Comment puis-je appliquer les modifications à ce service et aux autres composants aléatoires de la page être au courant de ces modifications? Lutte contre ce problème récemment
Construire un service avec 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);
}
};
});
Ensuite, abonnez-vous simplement aux modifications.
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();
};
});
Les clients peuvent s'abonner aux modifications avec DataService.subscribe
et les producteurs peuvent les modifier avec DataService.set
.
La DEMO sur PLNKR .
L'objet $watch
est disponible dans l'objet $scope
. Vous devez donc ajouter $scope
dans la fonction de fabrique de votre contrôleur, puis placer l'observateur sur la variable.
$scope.$watch(function(){
return myBox.game;
}, function(newVal){
alert('Value changed to '+ newVal)
});
Remarque: Je sais que vous avez converti
directive
encomponent
, pour supprimer la dépendance de$scope
afin que vous puissiez vous rapprocher de Angulaire2. Mais il semble que cela n’a pas été supprimé pour cette affaire . </ strike>
Mettre à jour
Fondamentalement angulaire 1.5, la méthode .component
ajoutée permet de différencier deux fonctionnalités différentes. Comme component
. Signifie exécuter un comportement particulier en ajoutant selector
, où en tant que directive
signifie ajouter un comportement spécifique au DOM. Directive est juste une méthode d'habillage sur .directive
DDO (directive Definition Object). Seul ce que vous pouvez voir, ils avaient la fonction remove link/compile
lorsqu’ils utilisaient la méthode .component
où vous pouviez obtenir un DOM compilé angulaire.
Utilisez le crochet $onChanges
$doCheck
lifecycle du crochet du cycle de vie des composants angulaires, ceux-ci seront disponibles après la version angulaire 1.5.3+.
_/$ onChanges (changesObj) - Appelé chaque fois que les liaisons sont mises à jour. ChangesObj est un hachage dont les clés sont les noms des propriétés liées.
$ doCheck () - Appelé à chaque tour du cycle de résumé lorsque la liaison est modifiée. Fournit une opportunité de détecter et d'agir sur les changements.
En utilisant la même fonction dans le composant, votre code sera compatible pour passer à Angular 2.
Pour les personnes intéressées par ma solution, je finis par avoir recours aux observables RXJS, que vous devrez utiliser lorsque vous obtiendrez Angular 2. Voici un fiddle efficace pour la communication entre composants qui me permet de mieux contrôler les éléments à surveiller.
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);
}
}
}])
Un bref avertissement concernant l'utilisation de ng-change
, comme recommandé avec la réponse acceptée, avec une composante angulaire de 1,5.
Si vous devez surveiller un composant dont ng-model
et ng-change
ne fonctionnent pas, vous pouvez transmettre les paramètres comme suit:
Balisage dans lequel le composant est utilisé:
<my-component on-change="$ctrl.doSth()"
field-value="$ctrl.valueToWatch">
</my-component>
Composant js:
angular
.module('myComponent')
.component('myComponent', {
bindings: {
onChange: '&',
fieldValue: '='
}
});
Balisage des composants:
<select ng-model="$ctrl.fieldValue"
ng-change="$ctrl.onChange()">
</select>
Je suis en retard. Mais cela peut aider un autre peuple.
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
});
}]
});
Disponible dans IE11, MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver . Vous devez injecter un service $ element dans le contrôleur, ce qui semi-rompt la séparation DOM/contrôleur, mais j'estime qu'il s'agit d'une exception fondamentale (c.-à-d. Un défaut) dans angularjs. Puisque masquer/afficher est asynchrone, nous avons besoin d’un rappel sur l’émission, que angularjs et angular-bootstrap-tab ne fournissent pas. Vous devez également savoir quel élément du DOM vous souhaitez observer. J'ai utilisé le code suivant pour le contrôleur angularjs pour déclencher le reflow du graphique 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']
})
Très bonne réponse acceptée, mais je pourrais ajouter que vous pouvez aussi utiliser le pouvoir des événements (un peu comme dans le signal/les slots Qt si vous voulez).
Un événement est diffusé: $rootScope.$broadcast("clickRow", rowId)
de n’importe quel parent (ou même de contrôleur enfants) . Dans votre contrôleur, vous pouvez gérer l’événement de la manière suivante:
$scope.$on("clickRow", function(event, data){
// do a refresh of the view with data == rowId
});
Vous pouvez également ajouter une connexion à cela comme ceci (pris à partir d'ici: 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;
});
}
});