58 votes

AngularJS utilise $rootScope comme magasin de données

J'ai une idée pour mon application AngularJS et je suis curieux de savoir si la communauté AngularJS considérerait qu'il est acceptable de procéder de cette façon. En bref, je me connecte à une API de données et j'affiche mes résultats sur la page.

J'ai créé un service AngularJS qui crée un magasin de données sur $rootScope.DataStore . J'ai également une méthode de service qui met à jour le DataStore avec les données renvoyées par un point de terminaison de l'API. Si je demande le point de terminaison "products" de l'API à l'intérieur de mon contrôleur avec la méthode DataStore.update('products') , ce qui permettrait de mettre à jour $rootScope.DataStore.products avec les données de mon produit.

Maintenant, dans la vue/partiel, tout ce que j'ai à faire, c'est de dire ng-repeat="product in DataStore.products" pour afficher mes données, et ce quelle que soit la portée du contrôleur dans lequel je me trouve. Par essence, mon DataStore est donc ma seule source de vérité.

J'ai l'impression que cette méthode me permet de bénéficier d'une sémantique facile à suivre et d'un codage minimal du contrôleur. Ainsi, chaque fois que le DataStore est mis à jour, tout ce qui est lié au DataStore est également mis à jour.

Cela ne risque-t-il pas d'alourdir la charge de travail de l $rootScope ou s'agit-il simplement d'une façon étrange de procéder ? Ou est-ce une façon totalement géniale ? :) Tous les commentaires sont les bienvenus.

90voto

sh0ber Points 6566

Cette question est abordée dans le FAQ AngularJS cité ici :

Il y a parfois des données que vous n'êtes pas en mesure d'utiliser. l'ensemble de l'application. Pour ces données, vous devez peut injecter $rootScope a comme n'importe quel autre champ d'application. Puisque les champs d'application héritent du champ d'application racine, ces valeurs seront disponibles pour les expressions attachées aux directives comme ng-show, tout comme les valeurs de votre $scope local.

Il semble que l'équipe encourage l'utilisation de $rootScope de cette manière, avec cette mise en garde :

Bien sûr, l'état global craint et vous devriez utiliser $root. comme vous le feriez (espérons-le) avec les variables globales dans n'importe quel langage. En particulier, ne l'utilisez pas pour du code, mais uniquement pour des données. Si vous êtes tenté de mettre une fonction sur $rootScope, il est presque toujours préférable de la mettre dans un service qui peut être injecté là où il est nécessaire et plus facilement testé. testé.

Inversement, ne créez pas un service dont le seul but dans la vie est de de stocker et de renvoyer des données.

Cela permet de ne pas trop solliciter le $digest (qui met en œuvre une vérification de base pour tester la mutation des données) et ce n'est pas une façon étrange de faire les choses.

EDIT : Pour plus de détails sur les performances, voir cette réponse de Misko (AngularJS dev) ici sur SO : Comment fonctionne la liaison de données dans AngularJS ? On notera en particulier la section sur les performances.

34voto

Breck421 Points 552

Pour satisfaire toutes les parties, pourquoi ne pas utiliser $cacheFactory. Cela permet aux services de demande de données d'être sans état et de n'être qu'un getter et un setter. J'admets que garder les données dans le $rootScope ou en tant que propriété du service est pratique, mais je ne me sens pas à l'aise. L'utilisation de $cacheFactory est également très simple.

Créez d'abord un service de cache :

angular.module('CacheService', ['ng'])
    .factory('CacheService', function($cacheFactory) {
    return $cacheFactory('CacheService');
});

Incluez le fichier js dans votre app.js et injectez-le dans votre déclaration d'application :

var MyApp = angular.module('MyApp', ['CacheService']);

L'injecter dans le service, l'utiliser comme suit :

'use strict'

MyApp.factory('HackerNewsService', function(CacheService) {
    return {
        getNews: function(key) {
            var news = CacheService.get(key);

            if(news) {
                return news;
            }

            return null;
        },
        setNews: function(key, value) {
            CacheService.put(key, value);
        },
        clearNews: function(key) {
            CacheService.put(key, '');
        }
    };
});

Il ne vous reste plus qu'à injecter votre HackerNewsService dans votre contrôleur et à l'utiliser en appelant les méthodes que nous avons créées. Par exemple :

HackerNewsService.setNews('myArticle', {headline: 'My Article', body: 'This is the body'});
$scope.article = HackerNewsService.getNews('myArticle');

9voto

Ruben Decorp Points 56

D'après mon expérience, l'utilisation de $rootScope pour stocker la partie de mon modèle de données qui est commune à toutes les ngViews de mon application est la méthode la plus pratique.

<div>{{mymodel.property}}</div>

est pour moi plus lisible et plus court que

<div>{{getPropertyModel()}}</div>

avec le javascript

app.factory('MyModel', function(){
    return {
        getModel: function() { ... },
        setModel: function(m) { ... },
    }
});

app.controller('ctrl', ['$scope', 'MyModel', function($scope, MyModel){
    $scope.getPropertModel = function() {
        return MyModel.getModel().property;
    };
}]);

Si l'on utilise un service ou un cachefactory, chaque accès au modèle dans le modèle html devient une fonction, ce qui est moins lisible qu'un accès à une propriété du rootScope. L'utilisation du $rootScope permet de réduire le code et, par conséquent, les erreurs et les tests.

Bien entendu, seule la partie commune à tous les ngView est stockée dans $rootScope. Le reste du modèle est stocké dans le $scope local.

Les veilles sur une fonction sont également plus lentes que sur les propriétés d'un objet. Du point de vue des performances, $rootScope est donc meilleur.

0voto

Jason Points 228

Je ne comprends pas très bien pourquoi vous avez besoin d'utiliser le rootScope ? La durée de vie d'une instance de service est également celle de l'application entière, donc tout schéma de données / sémantique que vous utilisez pourrait également être caché dans le service lui-même et il serait partagé à travers les contrôleurs. L'une ou l'autre de ces méthodes ne survit cependant pas à l'actualisation comme le ferait l'utilisation d'un stockage local.

Le reste ressemble à une approche de chargement paresseux. Le service est le seul à savoir si les données ont été chargées à partir du serveur distant et les renvoie si elles ont déjà été mises en cache, et les met en cache et les renvoie si elles n'ont pas été mises en cache. Si je comprends bien cette partie, c'est un bon modèle.

éditer : Ici, j'adopte une approche similaire à celle du chargement paresseux, mais je remarque que le cache se trouve dans le service lui-même :

angular.module('HN_Reddit_Mashup.Services', [])
    .factory('HackerNews', function($http) {
        var HackerNewsCache = {};
        return {
            get: function(url) {
                return HackerNewsCache[url] ||
                    (HackerNewsCache[url] = $http.jsonp("http://api.thriftdb.com/api.hnsearch.com/items/_search?q=" + url +     "&callback=JSON_CALLBACK"));
            },                
        };
    })

0voto

Matty J Points 1040

Je suis confronté au même problème, et il me semble que le stockage dans un "emplacement" disponible globalement est la bonne approche, mais que $rootScope n'est pas l'endroit idéal.

Je viens de faire des recherches à ce sujet, et au lieu de stocker vos données sur $rootScope, vous pourriez envisager d'utiliser un "service" pour gérer vos données / préoccupations séparées, comme décrit ici (en particulier le dernier exemple de code) : http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/

Ensuite, dans le "service" que vous créez en utilisant cette approche, que vous sauvegardiez les données en mémoire, cacheFactory, stockage local (comme il a été fait allusion à aquí ), et/ou à votre base de données (par exemple via AJAX), dépend de ce qui convient aux besoins de votre application. Cela signifie également que des changements dans la manière dont vous stockez vos données peuvent être effectués indépendamment, si nécessaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X