AngularJS с использованием $rootScope в качестве хранилища данных

У меня есть идея для моего приложения AngularJS, и мне любопытно, будет ли сообщество AngularJS рассматривать это нормально, чтобы сделать это таким образом...

Короче говоря, я подключаюсь к API данных и показываю результаты на странице. Я создал службу angular, которая создает хранилище данных в $rootScope.DataStore. У меня также есть метод службы, который обновляет DataStore с данными, возвращаемыми с конечной точки API. Если я запрошу конечную точку API "продуктов" из моего контроллера с помощью DataStore.update( "продукты" ), это приведет к обновлению данных $rootScope.DataStore.products с данными моего продукта. Теперь, в представлении/частичном, все, что мне нужно сделать, это сказать ng-repeat = "product in DataStore.products", чтобы показать мои данные, и не имеет значения, в какой области контроллера я нахожусь. Таким образом, по существу мой DataStore мой единственный источник истины.

Я чувствую, что получаю от этого метода легко следовать семантике и минимальному кодированию контроллера. Таким образом, в любое время, когда DataStore обновляется, все, что связано с DataStore, также обновляется.

Будет ли это слишком сильно загружаться в цикл digest $rootScope, или это просто странный способ сделать это? Или это потрясающе?:) Любые комментарии приветствуются.

Ответ 1

Этот вопрос рассматривается в Часто задаваемые вопросы по AngularJS:

Иногда есть фрагменты данных, которые вы хотите сделать глобальными все приложение. Для этого вы можете вводить $rootScope и устанавливать значения на он, как и любая другая область. Поскольку области, наследуемые от области корня, эти значения будут доступны выражениям, прилагаемым к директивы, такие как ng-show, точно так же, как значения в локальной области $scope.

Кажется, что команда поощряет использование $rootScope таким образом, с этим предупреждением:

Конечно, глобальное состояние отстойно, и вы должны использовать $rootScope экономно, как вы бы (надеюсь) использовали с глобальными переменными на любом языке. В частности, не используйте его для кода, а только данные. Если у вас возникает соблазн положил функцию на $rootScope, почти всегда лучше положить ее в услуги, которые можно вводить там, где это необходимо, и более легко тестирование.

И наоборот, не создавайте службу, единственной целью которой является хранить и возвращать бит данных.

Это не слишком сильно нагружает цикл $digest (который реализует базовую грязную проверку для проверки мутации данных), и это не странный способ сделать что-то.

EDIT: Подробнее об эффективности см. в этом ответе от Misko (AngularJS dev) здесь, на SO: Как работает привязка данных в AngularJS? Обратите внимание на раздел о производительности.

Ответ 2

Чтобы успокоить всех сторон, почему бы просто не использовать $cacheFactory. Это позволяет службам запросов данных быть без гражданства и в основном просто получателем и установщиком. Я признаю, что хранить данные на $rootScope или как свойство в сервисе удобно, но просто чувствует себя не так. Использование $cacheFactory довольно просто.

Сначала создайте службу кеша:

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

Включите js файл в свой app.js, а затем введите его в декларацию приложения:

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

Внесите его в службу, используйте его так:

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

Теперь все, что вам нужно сделать, это ввести HackerNewsService в свой контроллер и использовать его, вызвав методы, которые мы создали на нем. Например:

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

Ответ 3

Мой опыт заключается в том, что использование $rootScope для хранения части моей datamodel, которая является общей для всех ngView в моем приложении, является наиболее удобным способом.

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

для меня более читабельна и короче

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

с 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;
    };
}]);

Если вы пользуетесь службой или кешью, каждый доступ к модели в html-шаблоне становится функцией, которая менее читаема, чтобы получить доступ к свойству rootScope. Использование $rootScope дает меньше кода и, как следствие, меньше ошибок и меньше тестирования.

Конечно, только общая часть всего ngView хранится в $rootScope. Остальная часть модели хранится в локальной области $.

Часы на функции также медленнее, чем на свойства объекта. Таким образом, лучше работать с $rootScope.

Ответ 4

Я думаю, я не уверен, зачем вам нужно использовать rootScope? Срок службы экземпляра службы также является всем приложением, поэтому любая схема/семантика данных, которую вы используете, также может быть спрятана прямо в самой службе, и она будет доступна для всех контроллеров. Однако любой из этих методов не выдерживает обновления, как использование локального хранилища.

Остальная часть звучит как ленивый подход к загрузке. Где услуга является единственной вещью, "осведомленной" о том, загружены ли данные с удаленного и возвращает ли она, если она уже кэширована и кэшируется и возвращает ее, если ее нет? Если я правильно понимаю эту часть, это хороший образец.

изменить: Здесь я придерживаюсь аналогичного подхода к ленивой загрузке, замечаю, что кеш находится только в самой службе:

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

Ответ 5

Я просто сталкиваюсь с одной и той же проблемой, и мне кажется, что сохранение ее глобального доступного "местоположения" является правильным подходом, но этот $rootScope не является идеальным местом.

Я только что исследовал это больше, и вместо того, чтобы хранить ваши данные в $rootScope, вы можете рассмотреть возможность использования "службы" для управления своими данными/отдельными проблемами, как описано здесь (особенно последний пример кода): http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/

Затем в "службе", которую вы создаете с использованием этого подхода, сохраняете ли вы данные в памяти, cacheFactory, localstorage (как указано в здесь) и/или в вашу БД (например, через AJAX), зависит от потребностей вашего приложения. Это также означает, что изменения в том, как вы храните свои данные, могут быть сделаны независимо, по мере необходимости.