AngularJS utilise $rootScope comme un magasin de données

j'ai une idée pour mon application AngularJS et je suis curieux de savoir si la communauté AngularJS serait d'accord pour le faire 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 angulaire qui crée un magasin de données sur $rootScope.La banque de données. J'ai aussi une méthode de service qui met à jour le DataStore avec les données retournées à partir d'un terminal API. Si je demande le point final de L'API "produits" depuis l'intérieur de mon contrôleur avec le Magasin de données.update ('products'), cela mettrait à jour $rootScope.La banque de données.produits avec mes données produit. Maintenant, dans la vue / partielle, tout ce que je dois faire est de dire ng-repeat="produit dans DataStore.produits" pour montrer mes données, et il n'a pas d'importance dans quelle portée contrôleur je suis. Donc, en essence, mon DataStore est ma seule source de vérité.

ce que je sens comme je gagne de cette méthode est facile à suivre la sémantique et le codage de contrôleur minimal. Donc, chaque fois que le DataStore est mis à jour, n'importe quoi qui est lié à DataStore est également mis à jour.

est-ce que cela placerait trop de charge sur le cycle $rootScope digest, ou est-ce juste une façon étrange de le faire? Ou est-ce une totalement génial? :) Tout les commentaires sont les bienvenus.

57
demandé sur Chris Collins 2013-05-24 19:58:23

5 réponses

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

de temps en temps il ya des morceaux de données que vous voulez faire global à l'ensemble de l'application. Pour ceux-ci, vous peut injecter $rootScope et définir des valeurs sur comme tout autre portée. Puisque les portées héritent de la portée racine, ces valeurs seront disponibles pour les expressions les directives comme ng-montrent exactement les mêmes valeurs votre local $champ d'application.

il semble que l'équipe encourage l'utilisation de $rootScope de cette façon, avec cette mise en garde:

bien sûr, global state craint et vous devriez utiliser $rootScope avec parcimonie, comme vous l'utiliseriez (avec un peu de chance) avec des variables globales dans n'importe quelle langue. En particulier, ne l'utilisez pas pour le code, seulement des données. Si vous êtes tenté d' mettre une fonction $rootScope, il est presque toujours préférable de le mettre dans un un service qui peut être injecté où c'est nécessaire, et plus facilement testé.

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

cela ne met pas trop de pression sur le cycle $digest (qui implémente le contrôle sale de base pour tester la mutation des données) et ce n'est pas une façon étrange de faire les choses.

modifier: pour plus de détails sur la performance, voir ce réponse de Misko (AngularJS dev) ici sur SO: Comment fonctionne la liaison de données à AngularJS? Note spécifiquement la section sur la performance.

88
répondu sh0ber 2017-05-23 12:02:35

pour apaiser toutes les parties, pourquoi ne pas simplement utiliser le $cacheFactory. Cela permet aux services de demande de données d'être apatrides et essentiellement juste un getter et setter. Je vais admettre garder les données sur le $rootScope ou comme une propriété dans le service est commode mais se sent juste mal. Utiliser $cacheFactory est assez facile aussi.

créer D'abord un service de cache:

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

incluez le fichier js dans votre application.JS et puis injectez-le en vous déclaration app:

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

injectez-le dans le service, utilisez-le comme ceci:

'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, '');
        }
    };
});

Maintenant, tout ce que vous avez à faire est d'injecter votre HackerNewsService dans votre contrôleur et de l'utiliser en appelant les méthodes que nous avons créé. Par exemple:

HackerNewsService.setNews('myArticle', {headline: 'My Article', body: 'This is the body'});
$scope.article = HackerNewsService.getNews('myArticle');
32
répondu Jordan Papaleo 2013-10-01 15:10:01

mon expérience est que l'utilisation du $ rootScope pour stocker la partie de mon datamodel qui est commun à tous les ngViews dans mon application, est la manière la plus commode.

<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 on utilise un service ou un répertoire, chaque accès au modèle dans le modèle html devient une fonction, qui est moins lisible qu'un accès une propriété du rootScope. Utiliser $rootScope donne moins de code, et par conséquent moins d'erreurs et moins de tests.

bien sûr, seule la partie commune de tous les ngView est stockée dans $rootScope. Le reste du modèle est stocké dans le $scope local.

Montres sur une fonction sont également plus lents que sur les propriétés de l'objet. Donc, du point de vue de la performance, $rootScope est aussi meilleur.

8
répondu Ruben Decrop 2013-11-18 14:57:30

je suppose que je ne suis pas sûr pourquoi vous avez besoin d'utiliser le rootScope? La durée de vie d'une instance de service est l'ensemble de l'application ainsi, quel que soit le schéma de données / sémantique que vous utilisez pourrait également être caché directement dans le service lui-même et il serait partagé entre les contrôleurs. L'une ou l'autre de ces méthodes ne survivent cependant pas au rafraîchissement comme le ferait un stockage local.

le reste ressemble à une approche de chargement paresseux. Où le service est la seule chose "conscient" de si les données ont été chargées à partir de la télécommande et si elle est déjà mise en cache et mise en cache et renvoie si elle ne l'est pas? Si je comprends bien cette partie, c'est un bon schéma.

modifier: Ici, j'adopte une approche similaire au chargement paresseux, remarquez que le cache est juste 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"));
            },                
        };
    })
0
répondu Jason 2013-05-24 16:17:52

je suis juste face à la même question, et il me semble que le stockage de l'information globalement disponible "emplacement" est la bonne approche, mais que $rootScope n'est pas l'endroit idéal.

je viens de faire des recherches plus, 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 /

puis dans le" service "que vous créez en utilisant cette approche, si vous enregistrez les données en mémoire, cacheFactory, localstorage (comme fait allusion à ici ), et/ou à votre DB (par ex. via AJAX), est ce qui convient aux besoins de votre application. Cela signifie également que des modifications à la façon dont vous stockez vos données peuvent être apportées indépendamment, selon les besoins.

0
répondu Matty J 2017-05-23 12:25:52