AngularJS: la manière correcte de se lier aux propriétés d'un service

162

Je recherche les meilleures pratiques sur la façon de se lier à une propriété de service dans AngularJS.

J'ai travaillé sur plusieurs exemples pour comprendre comment se lier à des propriétés dans un service créé à l'aide d'AngularJS.

Ci-dessous, j'ai deux exemples de la manière de se lier à des propriétés dans un service; ils fonctionnent tous les deux. Le premier exemple utilise des liaisons de base et le deuxième exemple utilise $ scope. $ Watch pour se lier aux propriétés du service

L'un de ces exemples est-il préféré lors de la liaison à des propriétés dans un service ou existe-t-il une autre option dont je ne suis pas au courant qui serait recommandée?

Le principe de ces exemples est que le service devrait mettre à jour ses propriétés «lastUpdated» et «calls» toutes les 5 secondes. Une fois les propriétés du service mises à jour, la vue doit refléter ces changements. Ces deux exemples fonctionnent avec succès; Je me demande s'il existe une meilleure façon de procéder.

Reliure de base

Le code suivant peut être affiché et exécuté ici: http://plnkr.co/edit/d3c16z

<html>
<body ng-app="ServiceNotification" >

    <div ng-controller="TimerCtrl1" style="border-style:dotted"> 
        TimerCtrl1 <br/>
        Last Updated: {{timerData.lastUpdated}}<br/>
        Last Updated: {{timerData.calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.timerData = Timer.data;
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

L'autre façon dont j'ai résolu la liaison aux propriétés du service est d'utiliser $ scope. $ Watch dans le contrôleur.

$ scope. $ watch

Le code suivant peut être affiché et exécuté ici: http://plnkr.co/edit/dSBlC9

<html>
<body ng-app="ServiceNotification">
    <div style="border-style:dotted" ng-controller="TimerCtrl1">
        TimerCtrl1<br/>
        Last Updated: {{lastUpdated}}<br/>
        Last Updated: {{calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.$watch(function () { return Timer.data.lastUpdated; },
                function (value) {
                    console.log("In $watch - lastUpdated:" + value);
                    $scope.lastUpdated = value;
                }
            );

            $scope.$watch(function () { return Timer.data.calls; },
                function (value) {
                    console.log("In $watch - calls:" + value);
                    $scope.calls = value;
                }
            );
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

Je sais que je peux utiliser $ rootscope. $ Broadcast dans le service et $ root. $ On dans le contrôleur, mais dans d'autres exemples que j'ai créés, l'utilisation de $ broadcast / $ lors de la première diffusion n'est pas capturée par le contrôleur, mais les appels supplémentaires qui sont diffusés sont déclenchés dans le contrôleur. Si vous connaissez un moyen de résoudre le problème de diffusion de $ rootscope. $, Veuillez fournir une réponse.

Mais pour reformuler ce que j'ai mentionné plus tôt, j'aimerais connaître la meilleure pratique de liaison à des propriétés de service.


Mettre à jour

Cette question a été initialement posée et répondue en avril 2013. En mai 2014, Gil Birman a fourni une nouvelle réponse, que j'ai changée en réponse correcte. Étant donné que la réponse de Gil Birman a très peu de votes positifs, je crains que les personnes lisant cette question ne tiendront pas compte de sa réponse en faveur d'autres réponses avec beaucoup plus de votes. Avant de prendre une décision sur la meilleure réponse, je recommande vivement la réponse de Gil Birman.

Mike Barlow - BarDev
la source
Je pense que la réponse de Josh David Miller est meilleure que celle de Gil Birman. Et en utilisant $ watch, $ watchGroup et $ watchCollection peut même le rendre encore meilleur. La séparation des préoccupations est très importante sur les applications de taille moyenne à grande.
Jonathan
@bardev Je pense que les deux réponses n'étaient pas utiles, et les nouveaux développeurs les comprendraient complètement mal.
Zalaboza
Le problème que vous posez concerne le comportement des variables JavaScript natives des objets de référencement, j'ai ajouté une petite explication ci
Zalaboza

Réponses:

100

Considérez quelques avantages et inconvénients de la deuxième approche :

  • 0 {{lastUpdated}} au lieu de {{timerData.lastUpdated}}, ce qui pourrait tout aussi bien être {{timer.lastUpdated}}, ce que je pourrais dire est plus lisible (mais ne discutons pas ... je donne à ce point une note neutre pour que vous décidiez par vous-même)

  • +1 Il peut être pratique que le contrôleur agisse comme une sorte d'API pour le balisage, de sorte que si la structure du modèle de données change, vous pouvez (en théorie) mettre à jour les mappages d'API du contrôleur sans toucher au partiel html.

  • -1 Cependant, la théorie n'est pas toujours pratique et je me retrouve généralement à devoir modifier le balisage et la logique du contrôleur lorsque des changements sont nécessaires, de toute façon . Ainsi, l'effort supplémentaire d'écrire l'API annule son avantage.

  • -1 De plus, cette approche n'est pas très sèche.

  • -1 Si vous souhaitez lier les données à ng-modelvotre code, devenez encore moins DRY car vous devez reconditionner le $scope.scalar_valuesdans le contrôleur pour effectuer un nouvel appel REST.

  • -0.1 Il y a un petit coup de performance créant des observateurs supplémentaires. De plus, si des propriétés de données sont attachées au modèle qui n'ont pas besoin d'être surveillées dans un contrôleur particulier, elles créeront une surcharge supplémentaire pour les observateurs profonds.

  • -1 Et si plusieurs contrôleurs ont besoin des mêmes modèles de données? Cela signifie que vous avez plusieurs API à mettre à jour à chaque changement de modèle.

$scope.timerData = Timer.data;cela commence à sembler très tentant en ce moment ... Plongeons-nous un peu plus dans ce dernier point ... De quel genre de changements de modèle parlions-nous? Un modèle sur le back-end (serveur)? Ou un modèle qui est créé et ne vit que dans le front-end? Dans les deux cas, ce qui est essentiellement l' API de mappage de données appartient à la couche de service frontale (une usine ou un service angulaire). (Notez que votre premier exemple - ma préférence - n'a pas une telle API dans la couche de service , ce qui est bien car c'est assez simple, il n'en a pas besoin.)

En conclusion , tout n'a pas à être découplé. Et en ce qui concerne le découplage complet du balisage du modèle de données, les inconvénients l'emportent sur les avantages.


Les contrôleurs, en général , ne devraient pas être jonchés de $scope = injectable.data.scalar's. Ils devraient plutôt être saupoudrés de $scope = injectable.data's, promise.then(..)' s et $scope.complexClickAction = function() {..}'s

En tant qu'approche alternative pour réaliser le découplage des données et donc l'encapsulation de la vue, le seul endroit où il est vraiment logique de découpler la vue du modèle est avec une directive . Mais même là, n'utilisez pas de $watchvaleurs scalaires dans les fonctions controllerou link. Cela ne fera pas gagner du temps et ne rendra pas le code plus maintenable ni lisible. Cela ne facilitera même pas les tests car des tests robustes en angulaire testent généralement le DOM résultant de toute façon . Au lieu de cela, dans une directive, demandez votre API de données sous forme d'objet et privilégiez l'utilisation uniquement des $watchers créés par ng-bind.


Exemple http://plnkr.co/edit/MVeU1GKRTN4bqA3h9Yio

<body ng-app="ServiceNotification">
    <div style="border-style:dotted" ng-controller="TimerCtrl1">
        TimerCtrl1<br/>
        Bad:<br/>
        Last Updated: {{lastUpdated}}<br/>
        Last Updated: {{calls}}<br/>
        Good:<br/>
        Last Updated: {{data.lastUpdated}}<br/>
        Last Updated: {{data.calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.data = Timer.data;
            $scope.lastUpdated = Timer.data.lastUpdated;
            $scope.calls = Timer.data.calls;
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 500);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>

MISE À JOUR : Je suis enfin revenu sur cette question pour ajouter que je ne pense pas que l'une ou l'autre approche soit «mauvaise». À l'origine, j'avais écrit que la réponse de Josh David Miller était incorrecte, mais rétrospectivement, ses arguments sont tout à fait valables, en particulier son point sur la séparation des préoccupations.

Séparation des préoccupations mise à part (mais liée de manière tangentielle), il y a une autre raison pour la copie défensive que j'ai omis de considérer. Cette question concerne principalement la lecture de données directement à partir d'un service. Mais que se passe-t-il si un développeur de votre équipe décide que le contrôleur doit transformer les données de manière triviale avant que la vue ne les affiche? (La question de savoir si les contrôleurs doivent transformer les données est une autre discussion.) Si elle ne fait pas d'abord une copie de l'objet, elle peut involontairement provoquer des régressions dans un autre composant de vue qui consomme les mêmes données.

Ce que cette question met vraiment en évidence, ce sont les lacunes architecturales de l'application angulaire typique (et vraiment de toute application JavaScript): couplage étroit des préoccupations et mutabilité des objets. Je suis récemment devenu amoureux des applications d'architecture avec React et des structures de données immuables. Cela résout à merveille les deux problèmes suivants:

  1. Séparation des préoccupations : un composant consomme toutes ses données via des accessoires et dépend peu ou pas des singletons globaux (tels que les services angulaires), et ne sait rien de ce qui s'est passé au-dessus de lui dans la hiérarchie des vues.

  2. Mutabilité : tous les accessoires sont immuables, ce qui élimine le risque de mutation involontaire des données.

Angular 2.0 est maintenant sur la bonne voie pour emprunter fortement à React pour atteindre les deux points ci-dessus.

Gil Birman
la source
1
Plus j'ai travaillé avec AngularJS, plus je comprends. Je pense que les contrôleurs AngularJS doivent être aussi simples et fins que possible. En ajoutant $ montres dans le contrôleur, cela complique la logique. En référençant simplement la valeur dans le service, c'est beaucoup plus simple et semble être plus de la manière AngularJS. -
Mike Barlow - BarDev
3
Les "dix commandements" d'AngularJS. C'est déclaratif pour une raison.
pilau
La réponse de Josh David Miller n'est pas "INCORRECTE". Il y a un temps et une place pour tout.
JoshuaDavid
Je pense que vous avez raison, @FireCoding. Je prévois de mettre à jour la réponse.
Gil Birman
@GilBirman excellente réponse. Voudriez-vous écrire et donner un exemple pour la directive? Pour illustrer ce que vous avez dit, «le seul endroit où il est vraiment logique de découpler la vue du modèle est avec une directive. [...] Plutôt, dans une directive, demandez votre API de données sous forme d'objet, et privilégiez l'utilisation uniquement de $ observateurs créés par ng-bind. "
klode
78

De mon point de vue, ce $watchserait la meilleure pratique.

Vous pouvez en fait simplifier un peu votre exemple:

function TimerCtrl1($scope, Timer) {
  $scope.$watch( function () { return Timer.data; }, function (data) {
    $scope.lastUpdated = data.lastUpdated;
    $scope.calls = data.calls;
  }, true);
}

C'est tout ce dont vous avez besoin.

Étant donné que les propriétés sont mises à jour simultanément, vous n'avez besoin que d'une seule montre. De plus, comme ils proviennent d'un seul objet plutôt petit, je l'ai changé pour simplement regarder la Timer.datapropriété. Le dernier paramètre passé à lui $watchdit de vérifier l'égalité profonde plutôt que de simplement s'assurer que la référence est la même.


Pour fournir un peu de contexte, la raison pour laquelle je préférerais cette méthode à la valeur du service directement sur la portée est d'assurer une séparation adéquate des préoccupations. Votre vue ne devrait pas avoir besoin de savoir quoi que ce soit sur vos services pour fonctionner. Le travail du contrôleur est de tout coller ensemble; son travail est d'obtenir les données de vos services et de les traiter de la manière nécessaire, puis de fournir à votre vue les détails dont elle a besoin. Mais je ne pense pas que son travail consiste simplement à transmettre le service directement à la vue. Sinon, que fait même le contrôleur là-bas? Les développeurs AngularJS ont suivi le même raisonnement lorsqu'ils ont choisi de ne pas inclure de «logique» dans les modèles (par exemple les ifdéclarations).

Pour être juste, il y a probablement plusieurs perspectives ici et j'attends avec impatience d'autres réponses.

Josh David Miller
la source
3
Pouvez-vous élaborer un peu? Préférez-vous les $ montres car la vue est moins couplée au service? Ie, {{lastUpdated}}vs.{{timerData.lastUpdated}}
Mark Rajcok
2
@BarDev, à utiliser Timer.datadans un $ watch, Timerdoit être défini sur la portée $, car l'expression de chaîne que vous passez à $ watch est évaluée par rapport à la portée. Voici un plunker qui montre comment faire fonctionner cela. Le paramètre objectEquality est documenté ici - 3ème paramètre - mais pas vraiment trop bien expliqué.
Mark Rajcok
2
En termes de performances, $watchest assez inefficace. Voir les réponses sur stackoverflow.com/a/17558885/932632 et stackoverflow.com/questions/12576798/…
Krym
11
@Kyrm C'est probablement vrai dans certaines circonstances, mais lorsqu'il s'agit de performances, nous devons rechercher les améliorations de performances "cliniquement significatives", plutôt que simplement les améliorations généralisées et statistiquement significatives. S'il y a un problème de performances dans une application existante, il doit être résolu. Sinon, il ne s'agit que d'une optimisation prématurée, ce qui conduit à un code plus difficile à lire, sujet aux bogues, qui ne suit pas les meilleures pratiques et qui n'a aucun avantage démontré.
Josh David Miller
1
En supposant que l'observateur utilise une fonction pour appeler le getter, alors oui. Ça marche bien. Je suis également un partisan des services retournant des instances dans une collection, où les fonctionnalités getter et setter d'es5 fonctionnent plutôt bien.
Josh David Miller
19

En retard à la fête, mais pour les futurs Googleurs - n'utilisez pas la réponse fournie.

JavaScript a un mécanisme de passage d'objets par référence, alors qu'il ne transmet qu'une copie superficielle pour les valeurs "nombres, chaînes, etc.".

Dans l'exemple ci-dessus, au lieu de lier les attributs d'un service, pourquoi n'exposons-nous pas le service à l'étendue?

$scope.hello = HelloService;

Cette approche simple rendra angulaire capable de faire une liaison bidirectionnelle et toutes les choses magiques dont vous avez besoin. Ne piratez pas votre contrôleur avec des observateurs ou des balises inutiles.

Et si vous craignez que votre vue écrase accidentellement vos attributs de service, utilisez definePropertypour la rendre lisible, énumérable, configurable ou pour définir des getters et des setters. Vous pouvez gagner beaucoup de contrôle en rendant votre service plus solide.

Dernier conseil: si vous passez votre temps à travailler sur votre contrôleur plus que sur vos services, vous le faites mal :(.

Dans ce code de démonstration particulier que vous avez fourni, je vous recommande de faire:

 function TimerCtrl1($scope, Timer) {
   $scope.timer = Timer;
 }
///Inside view
{{ timer.time_updated }}
{{ timer.other_property }}
etc...

Éditer:

Comme je l'ai mentionné ci-dessus, vous pouvez contrôler le comportement de vos attributs de service en utilisant defineProperty

Exemple:

// Lets expose a property named "propertyWithSetter" on our service
// and hook a setter function that automatically saves new value to db !
Object.defineProperty(self, 'propertyWithSetter', {
  get: function() { return self.data.variable; },
  set: function(newValue) { 
         self.data.variable = newValue; 
         // let's update the database too to reflect changes in data-model !
         self.updateDatabaseWithNewData(data);
       },
  enumerable: true,
  configurable: true
});

Maintenant dans notre contrôleur si nous le faisons

$scope.hello = HelloService;
$scope.hello.propertyWithSetter = 'NEW VALUE';

notre service changera la valeur de propertyWithSetteret publiera également la nouvelle valeur dans la base de données en quelque sorte!

Ou nous pouvons adopter n'importe quelle approche que nous voulons.

Reportez-vous à la documentation MDN pour defineProperty.

Zalaboza
la source
À peu près sûr que c'est ce que je décrivais ci-dessus, avec $scope.model = {timerData: Timer.data};, simplement en l'attachant au modèle au lieu de directement sur Scope.
Scott Silvi
1
AFAIK, la référence d'objet js fonctionne pour tout dans Service. Codage comme celui-ci: $ scope.controllerVar = ServiceVar, tout ce que vous faites dans $ scope.controllerVar est également fait dans ServiceVar.
Kai Wang
@KaiWang est d'accord, sauf si vous décidez d'utiliser DefineAttribute, vous pouvez faire en sorte que vos services aient une fonction de réglage pour empêcher les contrôleurs de muter accidentellement vos données de service.
Zalaboza
12

Je pense que cette question a une composante contextuelle.

Si vous extrayez simplement des données d'un service et que vous diffusez ces informations vers sa vue, je pense que la liaison directe à la propriété du service est très bien. Je ne veux pas écrire beaucoup de code standard pour simplement mapper les propriétés du service aux propriétés du modèle à consommer à mon avis.

De plus, les performances angulaires sont basées sur deux choses. Le premier est le nombre de liaisons sur une page. Le second est le coût des fonctions getter. Misko en parle ici

Si vous avez besoin d'exécuter une logique spécifique à une instance sur les données du service (par opposition au massage des données appliqué dans le service lui-même), et que le résultat de cela a un impact sur le modèle de données exposé à la vue, alors je dirais qu'un $ watcher est approprié, car tant que la fonction n'est pas terriblement chère. Dans le cas d'une fonction coûteuse, je suggérerais de mettre en cache les résultats dans une variable locale (au contrôleur), d'effectuer vos opérations complexes en dehors de la fonction $ watcher, puis de lier votre portée au résultat de cela.

En guise de mise en garde, vous ne devriez pas suspendre de propriétés directement à partir de votre $ scope. La $scopevariable n'est PAS votre modèle. Il a des références à votre modèle.

Dans mon esprit, les "meilleures pratiques" pour simplement diffuser des informations d'un service à l'autre:

function TimerCtrl1($scope, Timer) {
  $scope.model = {timerData: Timer.data};
};

Et puis votre point de vue contiendrait {{model.timerData.lastupdated}}.

Scott Silvi
la source
attention à cette suggestion, quelqu'un qui n'est pas expert en javascript pourrait essayer de le faire avec une propriété qui est une chaîne. Dans ce cas, javascript ne fait pas de référence à l'objet, mais copie brute de la chaîne. (et c'est mauvais parce qu'il n'est pas mis à jour, s'il change)
Valerio
7
N'ai-je pas couvert cela avec ma «mise en garde» selon laquelle vous devriez toujours utiliser le point (ce qui signifie ne pas le suspendre à $ scope, mais à $ scope.model). Si vous avez $ scope.model.someStringProperty et que vous référencez model.someStringProperty dans votre vue, il sera mis à jour, car les observateurs internes seront sur l'objet, pas sur l'accessoire.
Scott Silvi
6

En me basant sur les exemples ci-dessus, j'ai pensé lancer une manière de lier de manière transparente une variable de contrôleur à une variable de service.

Dans l'exemple ci-dessous, les modifications apportées à la $scope.countvariable Controller seront automatiquement reflétées dans la countvariable Service .

En production, nous utilisons en fait la liaison this pour mettre à jour un identifiant sur un service qui récupère ensuite les données de manière asynchrone et met à jour ses variables de service. Une liaison supplémentaire signifie que les contrôleurs sont automatiquement mis à jour lorsque le service se met à jour.

Le code ci-dessous peut être vu fonctionner à http://jsfiddle.net/xuUHS/163/

Vue:

<div ng-controller="ServiceCtrl">
    <p> This is my countService variable : {{count}}</p>
    <input type="number" ng-model="count">
    <p> This is my updated after click variable : {{countS}}</p>

    <button ng-click="clickC()" >Controller ++ </button>
    <button ng-click="chkC()" >Check Controller Count</button>
    </br>

    <button ng-click="clickS()" >Service ++ </button>
    <button ng-click="chkS()" >Check Service Count</button>
</div>

Service / contrôleur:

var app = angular.module('myApp', []);

app.service('testService', function(){
    var count = 10;

    function incrementCount() {
      count++;
      return count;
    };

    function getCount() { return count; }

    return {
        get count() { return count },
        set count(val) {
            count = val;
        },
        getCount: getCount,
        incrementCount: incrementCount
    }

});

function ServiceCtrl($scope, testService)
{

    Object.defineProperty($scope, 'count', {
        get: function() { return testService.count; },
        set: function(val) { testService.count = val; },
    });

    $scope.clickC = function () {
       $scope.count++;
    };
    $scope.chkC = function () {
        alert($scope.count);
    };

    $scope.clickS = function () {
       ++testService.count;
    };
    $scope.chkS = function () {
        alert(testService.count);
    };

}
TomDotTom
la source
C'est une solution géniale, merci, cela m'a beaucoup aidé, une façon très intelligente de le faire :)
Javis Perez
2

Je pense que c'est une meilleure façon de se lier au service lui-même au lieu des attributs qu'il contient.

Voici pourquoi:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>
<body ng-app="BindToService">

  <div ng-controller="BindToServiceCtrl as ctrl">
    ArrService.arrOne: <span ng-repeat="v in ArrService.arrOne">{{v}}</span>
    <br />
    ArrService.arrTwo: <span ng-repeat="v in ArrService.arrTwo">{{v}}</span>
    <br />
    <br />
    <!-- This is empty since $scope.arrOne never changes -->
    arrOne: <span ng-repeat="v in arrOne">{{v}}</span>
    <br />
    <!-- This is not empty since $scope.arrTwo === ArrService.arrTwo -->
    <!-- Both of them point the memory space modified by the `push` function below -->
    arrTwo: <span ng-repeat="v in arrTwo">{{v}}</span>
  </div>

  <script type="text/javascript">
    var app = angular.module("BindToService", []);

    app.controller("BindToServiceCtrl", function ($scope, ArrService) {
      $scope.ArrService = ArrService;
      $scope.arrOne = ArrService.arrOne;
      $scope.arrTwo = ArrService.arrTwo;
    });

    app.service("ArrService", function ($interval) {
      var that = this,
          i = 0;
      this.arrOne = [];
      that.arrTwo = [];

      $interval(function () {
        // This will change arrOne (the pointer).
        // However, $scope.arrOne is still same as the original arrOne.
        that.arrOne = that.arrOne.concat([i]);

        // This line changes the memory block pointed by arrTwo.
        // And arrTwo (the pointer) itself never changes.
        that.arrTwo.push(i);
        i += 1;
      }, 1000);

    });
  </script>
</body> 

Vous pouvez le jouer sur ce plunker .

Trantor Liu
la source
1

Je préfère garder mes observateurs le moins possible. Ma raison est basée sur mes expériences et on pourrait la soutenir théoriquement.
Le problème avec l'utilisation des observateurs est que vous pouvez utiliser n'importe quelle propriété sur l'étendue pour appeler l'une des méthodes dans n'importe quel composant ou service que vous aimez.
Dans un projet du monde réel, vous vous retrouverez bientôt avec une chaîne non traçable (mieux dit difficile à tracer) de méthodes appelées et de valeurs modifiées, ce qui rend particulièrement tragique le processus d'intégration.

Reyraa
la source
0

Lier des données, qui envoie un service n'est pas une bonne idée (architecture), mais si vous en avez plus besoin, je vous suggère 2 façons de le faire

1) vous pouvez obtenir les données qui ne sont pas à l'intérieur de votre service.Vous pouvez obtenir des données à l'intérieur de votre contrôleur / directive et vous n'aurez aucun problème pour les lier n'importe où

2) vous pouvez utiliser les événements angularjs. Quand vous le souhaitez, vous pouvez envoyer un signal (à partir de $ rootScope) et l'attraper où vous le souhaitez. Vous pouvez même envoyer des données sur cet eventName.

Peut-être que cela peut vous aider. Si vous avez besoin de plus d'exemples, voici le lien

http://www.w3docs.com/snippets/angularjs/bind-value-between-service-and-controller-directive.html

Hazarapet Tunanyan
la source
-1

Qu'en est-il de

scope = _.extend(scope, ParentScope);

Où ParentScope est un service injecté?

Tomascharad
la source
-2

Les solutions les plus élégantes ...

app.service('svc', function(){ this.attr = []; return this; });
app.controller('ctrl', function($scope, svc){
    $scope.attr = svc.attr || [];
    $scope.$watch('attr', function(neo, old){ /* if necessary */ });
});
app.run(function($rootScope, svc){
    $rootScope.svc = svc;
    $rootScope.$watch('svc', function(neo, old){ /* change the world */ });
});

De plus, j'écris des EDA (Event-Driven Architectures), donc j'ai tendance à faire quelque chose comme ce qui suit [version simplifiée à l'extrême]:

var Service = function Service($rootScope) {
    var $scope = $rootScope.$new(this);
    $scope.that = [];
    $scope.$watch('that', thatObserver, true);
    function thatObserver(what) {
        $scope.$broadcast('that:changed', what);
    }
};

Ensuite, je mets un auditeur dans mon contrôleur sur le canal souhaité et maintiens simplement ma portée locale à jour de cette façon.

En conclusion, il n'y a pas vraiment de «meilleure pratique» - plutôt sa préférence principale - tant que vous gardez les choses SOLIDES et que vous utilisez un couplage faible. La raison pour laquelle je recommanderais ce dernier code est que les EDA ont le couplage le plus bas possible par nature. Et si vous n'êtes pas trop préoccupé par ce fait, évitons de travailler ensemble sur le même projet.

J'espère que cela t'aides...

Cody
la source