Passer la portée actuelle à un service AngularJS

106

Est-il correct de transmettre le "courant" $scopeà un service AngularJS?

Je suis dans la situation où j'ai un service $ sachant qu'il est consommé par un seul contrôleur, et j'aimerais avoir une référence à la portée du contrôleur dans les méthodes $ service elles-mêmes.

Est-ce philosophiquement correct?

Ou je ferais mieux de diffuser des événements sur $ rootScope et de faire en sorte que mon contrôleur les écoute?

SC
la source
1
Pouvez-vous nous dire plus précisément ce que vous essayez de faire? Peut-être que pousser la portée sur le service n'est pas du tout nécessaire?
ganaraj
Eh bien, ce n'est pas si difficile. Simplement, je voudrais pouvoir accéder aux $scopepropriétés et appeler en $scope.$applycas de besoin.
SC
De plus, disons que je veux appliquer les modifications qui proviennent du service $ à la portée $. J'espère que c'est plus clair maintenant.
SC
8
Je vous suggère de mettre les propriétés $ scope auxquelles vous souhaitez que votre service accède dans le service lui-même (au lieu de les avoir dans le contrôleur). Les services sont de meilleurs endroits pour stocker les modèles / données que les contrôleurs.
Mark Rajcok
@MarkRajcock J'essaie également de comprendre ce problème. Actuellement, j'appelle simplement un service et j'attache les données servies au contrôleur $scope... comment le contrôleur accède-t-il directement aux données du service et les transmet à la vue sans faire cela?
drjimmie1976

Réponses:

67

Pour informer le contrôleur lorsqu'un événement asynchrone se produit, utilisez les promesses angulaires .

Pour provoquer le $apply, vous n'avez pas besoin de la portée, vous pouvez appeler $rootScope.$apply, car il n'y a aucune différence en l'appelant dans une portée spécifique ou à la racine.

En ce qui concerne la lecture des variables, il serait préférable que vous receviez des paramètres. Mais vous pouvez également le lire à partir d'une portée en tant que paramètre d'objet, mais j'irais avec un paramètre, ce qui rendrait votre interface de service beaucoup plus claire.

Caio Cunha
la source
Je pense que c'est la réponse qui résout mieux mes doutes de débutant AngularJS.
SC
@Caio Cunha Pourriez-vous expliquer pourquoi ce n'est pas une bonne idée de passer une portée? J'ai exactement ce problème, je veux ajouter des choses $scopevia un appel à un service utilisant une executeSql()fonction asynchrone . En regardant dans 3 options (1) utiliser un rappel sur la fonction async, puis appeler $scope.$apply... cela fonctionne, mais c'est moche (2) passer $scopeà la fonction asynchrone, puis appeler theScope.$apply()... cela fonctionne aussi (3) utiliser une promesse. ..pas encore essayé. Pourquoi une promesse est-elle le meilleur moyen? Merci!
drjimmie1976
15

Je dirais que si votre fonctionnalité est spécifique à un seul contrôleur, vous n'avez pas besoin d'un service.

Les tâches des contrôleurs sont de manipuler le modèle spécifique alors qu'un service doit traiter des tâches globales. Je préfère m'en tenir à ce paradigme au lieu de mélanger les choses.

C'est ce que disent les documents

Un service

Les services angulaires sont des singletons qui effectuent des tâches spécifiques communes aux applications Web

Manette

Dans Angular, un contrôleur est une fonction JavaScript (type / classe) utilisée pour augmenter les instances de Angular Scope, à l'exclusion de la portée racine.

PS: En dehors de cela, si vous avez besoin de digérer, vous pouvez également injecter le $ rootScope dans votre service.

F Lekschas
la source
2
Merci. Excellente réponse. En approfondissant ma situation, le fait est que j'utilise Service parce que je veux un singleton. Il n'y a qu'un seul contrôleur qui consomme le service, mais ce contrôleur peut être instancié plusieurs fois au cours du cycle de vie de l'application, donc je veux vraiment que le service soit toujours dans le même état.
SC
Quoi qu'il en soit, la clarification sur l'appel $applyou $digestsur le $ rootScope a tout à fait un sens pour moi.
SC
1
Je le garderais toujours séparé dans un service pour le test.
bluehallu
Si la fonctionnalité est spécifique à une instance d'un contrôleur, vous pouvez ignorer l'implémentation d'un service, mais sinon non, car vous sacrifiez la possibilité d'avoir un état partagé entre ces instances. De plus, ce n'est pas parce qu'il existe un seul type de contrôleur aujourd'hui qu'il n'y aura pas de contrôleur différent qui pourra exploiter la fonctionnalité demain. De plus, un service peut éviter de gonfler le contrôleur. Il ne s'agit donc pas tant de savoir s'il y a un seul contrôleur, mais de quelle fonctionnalité est en question.
Nick le
9

Oui. Vous pouvez transmettre la portée $ au service lorsque vous l'initialisez. Dans le constructeur de service, vous pouvez attribuer la portée à quelque chose comme ceci._scope puis référencer la portée dans le service!

angular.module('blah').controller('BlahCtrl', function($scope, BlahService) {

    $scope.someVar = 4;

    $scope.blahService = new blahService($scope);

});

angular.module('blah').factory('blahService', function() {

    //constructor
    function blahService(scope) {
        this._scope = scope;

        this._someFunction()
    }

    //wherever you'd reference the scope
    blahService.prototype._someFunction = function() {

        this._scope['someVar'] = 5;

    }

    return blahService;

});
user12121234
la source
1
+1, cependant, j'aimerais voir un moyen de connaître automatiquement chaque contrôleur chaque $scopefois que ce contrôleur donné injecte le service - afin de ne pas avoir à appeler une méthode sur le service et à lui passer manuellement $scope.
Cody
2
@Cody Je ne le recommande pas car cela est contradictoire avec Dependency Injection
Coldstar
D'accord, +1 pour m'avoir abattu! Probablement les bouchers DIP aussi - je dirais, au risque d'être redondants.
Cody
Vous mélangez des services avec des usines ici. Cette solution utilise une usine, qui diffère d'un service. La principale différence est qu'un service renvoie un objet (singleton). Alors qu'une fabrique renvoie une fonction qui peut être instanciée ( new MyFunction()). La question portait sur un service, où l'appel newn'est pas une option.
Karvapallo
@Karvapallo Bon point. En guise de contrepoint, je crois que les services angulaires font référence à une usine, un service et un fournisseur (ng-wat)?
user12121234
6

Personnellement, je pense que passer $scopeà un service est une mauvaise idée , car cela crée une sorte de référence circulaire: le contrôleur dépend du service et le service dépend de l'étendue du contrôleur.

En plus d'être déroutants en termes de relations, des choses comme celle-ci finissent par gêner le ramasse-miettes.

Mon approche préférée consiste à placer un objet de domaine dans la portée du contrôleur et à le transmettre au service. De cette façon, le service fonctionne indépendamment du fait qu'il soit utilisé dans un contrôleur ou peut-être dans un autre service à l'avenir.

Par exemple, si le service est censé pousser et extraire des éléments d'un tableau errors, mon code sera:

var errors = [];
$scope.errors = errors;
$scope.myService = new MyService(errors);

Le service interagit alors avec le contrôleur en fonctionnant sur errors. Bien sûr, je dois faire attention à ne jamais effacer toute la référence du tableau, mais en fin de compte, c'est une préoccupation générale de JS.

Je ne voudrais jamais utiliser la diffusion $applyet / ou des choses similaires, car à mon avis, les bonnes pratiques OO l'emporteront toujours sur la magie angulaire.

Marco Faustinelli
la source
1
Ce code a-t-il une différence avec celui-ci?:$scope.errors = []; $scope.myService = new MyService($scope.errors);
Soldeplata Saketos
@SoldeplataSaketos - Oui, c'est vrai. errorsvit indépendamment de $scope. C'est tout l'intérêt de cette réponse. Veuillez vérifier le lien que j'ai fourni dans le texte. À votre santé.
Marco Faustinelli
1
Si je comprends bien votre code, $scope.errorspointe vers var errors, et les erreurs variables me semblent redondantes, car ce n'est qu'un autre pointeur. Une situation similaire je peux penser et qu'il est manifestement est redondant ce morceau de code: const errors = errors2 = errors3 = []; $scope.errors = errors;. Êtes-vous d'accord avec le fait qu'avec seulement le morceau de code que vous avez fourni, cela semble var errors = []redondant?
Soldeplata Saketos
Non ce n'est pas. Je me répète mot à mot: errorsvit indépendamment de $scope. Vous devez comprendre ce qu'est un objet de domaine, ainsi que ce qu'est une varaffectation. Si le lien que j'ai fourni n'est pas suffisant, il existe de nombreux autres documents disponibles.
Marco Faustinelli
Eh bien, cela dépendra uniquement de la mise en œuvre de la fonction MyService(errors). Dans ma compréhension, le service devrait générer un tableau de journalisation basé sur le paramètre (c'est-à-dire dans ce cas un pointeur). Pour moi, c'est un mauvais schéma, car les services sont des singletons anguleux. Si l'implémentation du service est bien programmée, elle doit générer le tableau dans une variable interne (pour rester un singleton). Par conséquent, il n'a aucun sens d'initialiser la variable en dehors du service.
Soldeplata Saketos