AngularJS : Comment regarder du service des variables?
J'ai un service à la, dire:
factory('aService', ['$rootScope', '$resource', function ($rootScope, $resource) {
var service = {
foo: []
};
return service;
}]);
Et je voudrais utiliser foo
pour contrôler une liste est au format HTML:
<div ng-controller="FooCtrl">
<div ng-repeat="item in foo">{{ item }}</div>
</div>
Pour le contrôleur de détecter quand aService.foo
est mis à jour, j'ai bricolé ce modèle où je ajouter de service de l'unité de commande $scope
et ensuite utiliser $scope.$watch()
:
function FooCtrl($scope, aService) {
$scope.aService = aService;
$scope.foo = aService.foo;
$scope.$watch('aService.foo', function (newVal, oldVal, scope) {
if(newVal) {
scope.foo = newVal;
}
});
}
Ce sent de longue main, et j'ai été le répéter dans chaque contrôleur qui utilise le service de variables. Est-il un meilleur moyen d'obtenir regarder de variables partagées?
- Vous pouvez passer d'un troisième paramètre $à regarder la valeur true à la profondeur de la montre service et toutes ses propriétés.
- $champ d'application.foo= service.toto est suffisant, vous pouvez perdre la ligne ci-dessus. Et ce qu'il fait à l'intérieur de $watch n'a pas de sens, si vous voulez affecter une nouvelle valeur à $champ d'application.foo viens de le faire...
- Pourriez-vous référence
aService.foo
dans le code html? (comme ceci: plnkr.co/modifier/aNrw5Wo4Q0IxR2loipl5?p=preview) - J'ai ajouté un exemple, sans les Rappels de dollars ou de montres, voir la réponse ci-dessous (jsfiddle.net/zymotik/853wvv7s)
- C'est une question très intéressante. Et j'ai aussi découvert que je pouvais heureusement lier un contrôle à "service.foo" (et puis il faudrait être mis à jour sans aucune montres), mais si ma commande "$champ d'application.foo = service.foo;" et j'ai tenu à "toto", alors il n'auriez jamais mis à jour. Très étrange. La grande question de bien.
- vous avez raison. Je pense que c'est en raison de la nature de Javascript, vous pouvez voir ce modèle à de nombreux autres endroits (et pas seulement dans Angulaire, mais en JS en règle générale). D'une part, de transférer la valeur de (et il n'est pas lié) et d'autre part, vous transfert d'un objet (ou de la valeur qui fait référence à l'objet...), et c'est pourquoi les propriétés sont correctement mis à jour (comme parfaitement illustré dans l'exemple de Zymotik ci-dessus).
Vous devez vous connecter pour publier un commentaire.
Vous pouvez toujours utiliser la bonne vieille pattern observer si vous voulez éviter la tyrannie et les frais généraux de
$watch
.Dans le service:
Et dans le contrôleur:
$destory
événement sur le champ d'application et ajouter un annuler l'inscription de la méthode deaService
$watch
vs modèle observateur est tout simplement choisir d'interroger ou de pousser, et est essentiellement une question de performance, afin de l'utiliser lorsque la performance est importante. Je utiliser le pattern observer lors de l'sinon je l'aurais à la "profondeur" de regarder des objets complexes. J'attache de l'ensemble des services de l' $champ d'application, au lieu de regarder unique de valeurs de la fonction. - Je éviter angulaire de dollars de regarder comme le diable, il y a assez de ce qui se passe dans les directives et dans natif angulaire de la liaison de données.getSet
approche. Soit utiliser unObject.defineProperties(object, ...)
ou si vous êtes à la recherche d'unpubSub
méthodologie, vous pouvez aussi bien utiliser$rootScope.$on('changed:someData', setSomeData)
&$rootScope.$broadcast('changed:someData', someData)
. POINTS IMAGINAIRES pour cette réponse 🙁Dans un tel scénario, où plusieurs/unkown objets pourraient être intéressés par les changements, utilisez
$rootScope.$broadcast
de l'élément en cours de modification.Plutôt que de créer votre propre registre des auditeurs (qui doivent être nettoyés sur divers $détruit), vous devez être en mesure de
$broadcast
à partir du service en question.Vous devez toujours le code de la
$on
gestionnaires dans chaque auditeur, mais le motif est découplée de multiples appels à$digest
et évite donc le risque de longue watchers.De cette façon, aussi, les auditeurs peuvent aller et venir de l' DOM et/ou différent de l'enfant étendues sans le service de changement de comportement.
** mise à jour: exemples **
Émissions ferait le plus de sens dans l'onglet "global" des services qui pourraient avoir un impact d'innombrables autres choses dans votre application. Un bon exemple est celui d'un Utilisateur de service où il y a un certain nombre d'événements qui pourraient avoir lieu tel que la connexion, la déconnexion, la mise à jour, ralenti, etc. Je crois que c'est où les émissions ont le plus de sens parce que tout peut écouter un événement, sans même l'injection du service, et il n'a pas besoin d'évaluer toutes les expressions ou mettre en cache les résultats d'inspecter pour des changements. Il vient de feux et oublie (donc, assurez-sûr que c'est un feu-et-oublier de notification, et non pas quelque chose qui nécessite une action)
Le service ci-dessus serait de diffuser un message à tous les portée lors de la save() la fonction s'est terminée et que les données soient valides. Sinon, si c'est un $ressource ou une soumission ajax, déplacer la diffusion d'appels à la fonction de rappel de sorte qu'il se déclenche lorsque le serveur a répondu. Émissions en fonction de ce modèle particulièrement bien parce que chaque auditeur attend simplement la manifestation sans la nécessité de contrôler la portée sur chaque $digest. L'auditeur pourrait ressembler à:
L'un des avantages de ceci est l'élimination de plusieurs montres. Si vous étiez en combinant les champs ou en découlant, des valeurs comme l'exemple ci-dessus, vous devez regarder à la fois les propriétés firstname et lastname. Regarder le getUser() la fonction ne fonctionne que si l'utilisateur de l'objet a été remplacé sur les mises à jour, il ne serait pas le feu si l'utilisateur de l'objet seulement de ses propriétés de mise à jour. Dans ce cas, vous auriez à faire une profonde regarder et ce qui est de plus en plus intensive.
$de diffusion envoie le message de la portée, il est appelé à descendre dans n'importe quel enfant étendues. Donc l'appeler à partir de $rootScope le feu sur chaque portée. Si vous étiez à $diffusé à partir de votre contrôleur du champ d'application, par exemple, il serait de feu que dans les étendues qui héritent de votre contrôleur de portée. $émettent va dans le sens inverse et se comporte de façon similaire à un DOM événement en ce qu'il les bulles du champ d'application de la chaîne.
Garder à l'esprit qu'il existe des scénarios où $de diffusion fait beaucoup de sens, et il existe des scénarios où $watch est une meilleure option, surtout si dans un isolat portée avec une très montre spécifique de l'expression.
Je suis en utilisant la même approche que @dtheodot mais en utilisant angulaire de la promesse au lieu de passer des rappels
Alors, où que juste l'utilisation
myService.setFoo(foo)
méthode de mise à jourfoo
sur le service. Dans votre contrôleur, vous pouvez l'utiliser comme:Deux premiers arguments de
then
sont des succès et des erreurs des rappels, la troisième est de prévenir de rappel.De référence pour q$.
$scope.$watch
sur un service de variable n'était pas en faisant semblant de travailler (la portée, j'ai été regarder sur était un modal qui a hérité de$rootScope
) - cela a fonctionné. Truc génial, merci pour le partage!Sans montres ou de l'observateur rappels (http://jsfiddle.net/zymotik/853wvv7s/):
JavaScript:
HTML
Ce JavaScript fonctionne comme nous le passage d'un objet de retour de service plutôt qu'une valeur. Lorsqu'un objet JavaScript est retourné à partir d'un service, Angulaire ajoute des montres de toutes ses propriétés.
Également noter que j'utilise la var self = this "comme j'ai besoin de garder une référence à l'objet d'origine lorsque l' $timeout s'exécute, sinon" il " fera référence à l'objet window.
$scope.count = service.count
ne fonctionne pas.$scope.data = service.data
<p>Count: {{ data.count }}</p>
var self = this;
? pourquoi ne pas simplement utiliserthis
self
ici. Il est inutile de les utiliser pour la création de la fonction, carthis
etself
font référence au même objet. Il est logique d'utiliserself
seulement à l'intérieur de la fonction, puis de nouveau il n'est pas nécessaire de l'utiliser parce que vous pouvez utiliser.bind(this)
et quand vous avez une situation où vous ne pouvez pas utiliser.bind()
seulement alors vous devez utiliserself
.this
lors de l'utilisation de la$timeout
fonction.self
pour la création de tout. Il pourrait être bon si vous ne comprenez pas commentthis
et étendues fonctionne, mais je n'ai jamais rien vu de tel dans toute bonne bibliothèque.Aussi loin que je peux dire, vous n'avez pas à faire quelque chose d'aussi élaboré que ça. Vous avez déjà attribué foo de ce service à votre portée et depuis foo est un tableau ( et à son tour un objet, qu'il est affecté par la référence! ). Donc, tout ce que vous devez faire est de quelque chose comme ceci :
Si certains, d'autres variables dans cette même touche Ctrl est personne à charge sur foo change alors oui, vous avez besoin d'une montre pour observer foo et apporter des modifications à cette variable. Mais tant que c'est une simple référence à regarder est inutile. Espérons que cette aide.
$watch
?$watch
de travailler avec une primitive. Au lieu de cela, j'ai défini une méthode sur le service qui retourne la valeur primitive:somePrimitive() = function() { return somePrimitive }
Et j'ai affecté un $champ d'application de la propriété de cette méthode:$scope.somePrimitive = aService.somePrimitive;
. Ensuite, j'ai utilisé le champ d'application de la méthode dans le code HTML:<span>{{somePrimitive()}}</span>
$scope.foo = aService.foo
n'a pas de mise à jour de la portée de la variable automatiquement.Je suis tombé sur cette question, à la recherche de quelque chose de similaire, mais je pense qu'il mérite une explication approfondie de ce qui se passe, ainsi que des solutions supplémentaires.
Quand angulaire d'expression tels que celui que vous avez utilisé est présent dans le code HTML, Angulaire établit automatiquement une
$watch
pour$scope.foo
, et permettra de mettre à jour le code HTML à chaque fois que$scope.foo
changements.Le non-dit problème ici est que l'une des deux choses affectent
aService.foo
tels que les changements ne sont pas détectées. Ces deux possibilités sont:aService.foo
est à un nouveau tableau à chaque fois, causant la référence est dépassée.aService.foo
est en cours de mise à jour d'une façon telle qu'un$digest
cycle n'est pas déclenché sur la mise à jour.Problème 1: Les Références Obsolètes
Compte tenu de la première possibilité, en supposant un
$digest
, siaService.foo
était toujours le même tableau, le définir automatiquement$watch
permettrait de détecter les changements, comme le montre l'extrait de code ci-dessous.Solution 1-a: assurez-vous que le tableau ou l'objet est la même objet sur chaque mise à jour
JS:
HTML:
Comme vous pouvez le voir, le ng-repeat soi-disant attaché à
aService.foo
ne met pas à jour lorsqueaService.foo
changements, mais la ng-repeat attaché àaService2.foo
ne. C'est parce que notre référence àaService.foo
est dépassée, mais notre référence àaService2.foo
ne l'est pas. Nous avons créé une référence à la table initiale avec$scope.foo = aService.foo;
, qui a ensuite été écartée par le service sur sa prochaine mise à jour, sens$scope.foo
n'est plus référencé le tableau que nous avons voulu plus.Cependant, bien qu'il existe plusieurs façons de vous assurer que la référence initiale est conservée dans le tact, parfois, il peut être nécessaire de modifier l'objet ou le tableau. Ou si la propriété de service fait référence à une primitive comme un
String
ouNumber
? Dans tous ces cas, on ne peut pas simplement compter sur une référence. Donc, ce que peut - nous faire?Plusieurs des réponses données précédemment déjà donner quelques solutions à ce problème. Cependant, je suis personnellement en faveur de l'utilisation de la méthode simple proposé par Jin et thetallweeks dans les commentaires:
Solution 1-b: Fixez le service de la portée, de la référence et de
{service}.{property}
dans le code HTML.Sens, il suffit de faire ceci:
HTML:
JS:
JS:
HTML:
De cette façon, le
$watch
résoudreaService.foo
sur chaque$digest
, qui sera la valeur correctement mise à jour.C'est le genre de ce que vous étiez en train de faire avec votre solution, mais dans une bien moindre ronde sur façon. Vous avez ajouté un inutile
$watch
dans le contrôleur qui met explicitementfoo
sur le$scope
chaque fois qu'elle change. Vous n'avez pas besoin de ce surplus de$watch
lorsque vous attachezaService
au lieu deaService.foo
à la$scope
, et de lier explicitement àaService.foo
dans le balisage.Maintenant que tout est bien et bon, en supposant un
$digest
cycle est appliqué. Dans mes exemples ci-dessus, j'ai utilisé Angulaire du$interval
service pour mettre à jour les tableaux automatiquement le coup d'envoi à un$digest
boucle après chaque mise à jour. Mais que faire si le service de variables (pour quelque raison que ce soit) ne sont pas mis à jour à l'intérieur de la "Angulaire monde". En d'autres termes, nous ne ont un$digest
cycle activé automatiquement chaque fois que le service des modifications de la propriété?Problème 2: Manque D'
$digest
De nombreuses solutions ici permettra de résoudre cette question, mais je suis d'accord avec Code Whisperer:
Donc, je préfère continuer à utiliser le
aService.foo
de référence dans le balisage HTML, comme dans le deuxième exemple ci-dessus, et ne pas avoir à enregistrer un supplément de rappel dans le Contrôleur.Solution 2: Utiliser un setter et getter avec
$rootScope.$apply()
J'ai été surpris personne n'a encore suggéré l'utilisation d'un setter et getter. Cette fonctionnalité a été introduite dans ECMAScript5, et a donc été autour depuis des années. Bien sûr, cela signifie que si, pour quelque raison que ce soit, vous avez besoin pour soutenir vraiment les vieux navigateurs, cette méthode ne fonctionnera pas, mais je suis comme les getters et les setters sont largement sous-utilisés en JavaScript. Dans ce cas particulier, ils pourraient être très utiles:
JS:
HTML:
Ici j'ai ajouté un "privé" de la variable dans la fonction de service:
realFoo
. Cette mise à jour et récupérées à l'aide de laget foo()
etset foo()
fonctions, respectivement, sur leservice
objet.Notez l'utilisation de
$rootScope.$apply()
dans la fonction de configuration. Cela garantit que Angulaires seront informés de tout changement àservice.foo
. Si vous obtenez 'inprog" erreurs de voir cette page de référence utile, ou si vous utilisez Angulaire >= 1.3, vous pouvez simplement utiliser$rootScope.$applyAsync()
.Aussi se méfier de cette si
aService.foo
est mis à jour très fréquemment, car cela pourrait affecter sensiblement les performances. Si le rendement serait un problème, vous pouvez configurer un observateur tendance est similaire pour les autres réponses ici à l'aide de l'incubateur.services
pas pour les propriétés qui appartiennent au service de lui-même.Vous pouvez insérer le service en $rootScope et regarder:
Dans votre contrôleur:
Autre option est d'utiliser une bibliothèque externe comme: https://github.com/melanke/Watch.JS
Fonctionne avec: IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Node.JS de Rhinocéros , de 1,7+
Vous pouvez observer l'évolution d'un, de plusieurs ou de tous les attributs de l'objet.
Exemple:
Vous pouvez regarder les changements au sein de l'usine elle-même, et ensuite diffuser un changement
Puis dans votre contrôleur:
De cette manière, vous mettez toutes les usine de code à l'intérieur de sa description, alors vous ne pouvez compter que sur la diffusion de l'extérieur
==Mise à JOUR==
Très simple maintenant dans $watch.
Stylet ici.
HTML:
JavaScript:
Bâtiment sur dtheodor de réponse vous pouvez utiliser quelque chose de similaire à l'-dessous pour vous assurer que vous n'oubliez pas d'annuler l'enregistrement de la fonction de rappel... Certains peuvent s'opposer à la réussite de l'
$scope
à un service si.Tableau.remove est une extension de la méthode qui ressemble à ceci:
Voici mon approche générique.
Dans Votre Contrôleur
Pour ceux qui comme moi à la recherche d'une solution simple, ce n'est presque exactement ce que vous attendez de l'aide normale $regarder dans les contrôleurs.
La seule différence, c'est qu'elle évalue la chaîne dans son contexte javascript et non pas sur un champ d'application spécifique. Vous aurez à injecter $rootScope dans votre service, mais il est seulement utilisé pour accrocher dans le recueil des cycles correctement.
alors que face à une situation semblable, j'ai regardé une fonction dans le champ d'application et a eu le retour de la fonction le service variable. J'ai créé un js fiddle. vous pouvez trouver le code ci-dessous.
Je suis venu à cette question, mais il s'est avéré que mon problème était que j'étais à l'aide de la méthode setInterval quand j'aurais été à l'aide de l'angle $intervalle de fournisseur. C'est également le cas pour setTimeout (utiliser $timeout à la place). Je sais que ce n'est pas la réponse à la discussion de la question, mais cela pourrait aider certains, comme il m'a aidé.
setTimeout
, ou de toute autre non-Angulaire de la fonction, mais il suffit de ne pas oublier de placer le code dans la fonction de rappel avec$scope.$apply()
.J'ai trouvé une très bonne solution sur l'autre thread avec un problème similaire, mais approche totalement différente. Source: AngularJS : $regarder à l'intérieur de la directive n'est pas de travailler quand $rootScope valeur est modifiée
Fondamentalement la solution il dit DE ne PAS utilisation
$watch
comme il est très lourd de la solution. Au lieu ils proposent d'utiliser$emit
et$on
.Mon problème était de regarder une variable dans mon service et de réagir en directive. Et avec la méthode ci-dessus, c'est très facile!
Mon module/service exemple:
Donc, fondamentalement, je regarder mon
user
- chaque fois qu'il est réglé à une valeur nouvelle, je$emit
unuser:change
état.Maintenant, dans mon cas, dans le directive j'ai utilisé:
Maintenant dans le directive j'écoute sur le
$rootScope
et sur le changement - je réagir respectivement. Très facile et élégant!//service: (rien de spécial ici)
//ctrl:
Un tout petit peu moche, mais j'ai ajouté de l'enregistrement de la portée des variables à mon service pour une bascule:
Et puis dans le contrôleur:
this
à partir de ce service au lieu deself
?return this;
de votre constructeur 😉Jetez un oeil à cette plunker:: c'est l'exemple le plus simple que je pouvais penser de
http://jsfiddle.net/HEdJF/
J'ai vu des terribles observateur patrons ici qui provoquent des fuites de mémoire sur les grandes applications.
Je suis peut-être un peu en retard mais c'est aussi simple que cela.
La fonction montre montres pour les changements de référence (types primitifs), si vous voulez regarder quelque chose comme de la matrice de pousser suffit d'utiliser:
someArray.push(someObj); someArray = someArray.splice(0);
Cette volonté de mise à jour de la référence et de la mise à jour de la montre à partir de n'importe où. Y compris les services de méthode de lecture.
Tout ce qui est un primitif sera mis à jour automatiquement.
Je suis en retard pour la partie, mais j'ai trouvé une plus belle façon de le faire que la réponse affichée ci-dessus. Au lieu de l'affectation d'une variable pour stocker la valeur de la prestation variable, j'ai créé une fonction attachée à la portée, qui retourne le service variable.
contrôleur
Je pense que cela va faire ce que vous voulez. Mon contrôleur continue à vérifier la valeur de mon service avec cette mise en œuvre. Honnêtement, c'est beaucoup plus simple que la réponse choisie.
J'ai écrit deux simples services d'utilité qui m'aident à suivre la qualité de service propriétés des changements.
Si vous souhaitez ignorer la longue explication, vous pouvez aller détroit de jsfiddle
JS:
Ce service est utilisé par le contrôleur de la manière suivante:
Supposons que vous disposez d'un service "testService" avec les propriétés "prop1', 'prop2', 'prop3'. Vous voulez regarder et d'attribuer à la portée "prop1" et "prop2'. Avec le service de surveillance, il va ressembler à ça:
JS:
Regarder obj est grande, mais il n'est pas assez, si vous avez le code asynchrone à votre service. Pour ce cas, j'utilise un deuxième utilitaire qui ressemble à ça:
JS:
Je serait les déclencher à la fin de mon async code pour déclencher l' $digest boucle.
Comme ça:
JS:
Donc, tout ça ensemble ressemblera à ceci (vous pouvez l'exécuter ou ouvrir le violon):
JS:
HTML: