Angular JS正确的工厂结构

时间:2014-08-10 07:38:55

标签: json angularjs angularjs-factory

我的AngularJS单页面应用程序中有一个工厂,它根据JSON文件解析给定日期,以便在季节中返回季节和周数。我目前在每个方法$http.get('content/calendar.json').success(function(data) {...中调用两次JSON文件。

无论有多少方法,我怎样才能将调用分解出来?

    emmanuel.factory('DayService', function($http, $q){
    var obj = {};
        obj.season = function(d){
            // receives a mm/dd/yyyy string parses against Calendar service for liturgical season
            d = new Date(d);
            var day = d.getTime();
            var promise = $q.defer();
            var temp;
            $http.get('content/calendar.json').success(function(data) {
                for (var i=0; i<data.calendar.seasons.season.length; i++){
                    var start = new Date(data.calendar.seasons.season[i].start);
                    var end = new Date(data.calendar.seasons.season[i].end);
                    end.setHours(23,59);
                    //sets the time to be the last minute of the season
                    if (day >= start && day <= end){
                        //if given time fits within start and end dates in calendar then return season
                        temp = data.calendar.seasons.season[i].name;
                        //console.log(temp);
                        promise.resolve(temp);
                        break;
                    }
                }
            });
            return promise.promise;
        }
        obj.weekInSeason = function(d){
            //receives a date format mm/dd/yyyy
            var promise = $q.defer();
            $http.get('content/calendar.json').success(function(data) {
                    for (var i=0; i<data.calendar.seasons.season.length; i++){
                    d = new Date(d);
                    var day = d.getTime();
                    var end = new Date(data.calendar.seasons.season[i].end);
                    end.setHours(23,59);
                    end = end.getTime();
                    var diff = end - day;

                    if (parseFloat(diff) > 0){
                        var start = new Date(data.calendar.seasons.season[i].start);
                        start = start.getTime();
                        var startDiff = day - start;
                        var week = parseInt(startDiff /(1000*60*60*24*7))+1;
                        promise.resolve(week);
                        break;
                    } 
                }
            });
            return promise.promise;
        }
        obj.getData = function (d) {
            console.log('DayService.getData')
            console.log(today)
            var data = $q.all([
                this.season(d),
                this.weekInSeason(d)
            ]);
            return data;          
        };
    return obj;
});

3 个答案:

答案 0 :(得分:2)

此解决方案假定content/calendar.json永远不会更改。

我有 answered a question ,它可以帮助您解决这个问题。基本上,您必须在应用程序引导之前获取所有必需的配置/设置。手动引导应用程序,这意味着您必须删除html中的ng-app指令。

步骤:

[ 1 ]按照我上面提到的已回答的问题中的说明创建 bootstrapper.js 。基本上,它应该如下所示(注意:如果您需要在应用程序之前在应用程序中添加更多设置,则可以在urlMap中添加更多配置URL):

angular.injector(['ng']).invoke(function($http, $q) {

  var urlMap = {
    $calendar: 'content/calendar.json'
  };

  var settings = {};

  var promises = [];

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

  angular.forEach(urlMap, function(url, key) {
    promises.push($http.get(url).success(function(data) {
      settings[key] = data;
    }));
  });

  $q.all(promises).then(function() {
    bootstrap(settings);
  }).catch(function() {
    bootstrap();
  });

  function bootstrap(settings) {
    appConfig.value('Settings', settings);

    angular.element(document).ready(function() {
      angular.bootstrap(document, ['app', 'app.settings']);
    });
  }

});

[ 2 ]假设主模块的名称在 app 中为app.js

angular.module('app', [])

.factory('DayService', function(Settings){
    var calendar = Settings.$calendar,
        season = calendar.seasons.season,
        obj = {};

    obj.season = function(d){
        var day = new Date(d).getTime(),
            start, end, value;

        for (var i = 0; i < season.length; i++){
            start = new Date(season[i].start);
            end = new Date(season[i].end);
            end.setHours(23,59);
            if (day >= start && day <= end){
                value = season[i].name;
                break;
            }
        }
        return value;
    };

    obj.weekInSeason = function(d){
        var day = new Date(d).getTime(),
            end, diff, start, startDiff, week;


        for (var i = 0; i < season.length; i++){
            end = new Date(season[i].end);
            end.setHours(23,59);
            end = end.getTime();
            diff = end - day;

            if (parseFloat(diff) > 0){
                start = new Date(season[i].start);
                start = start.getTime();
                startDiff = day - start;
                week = parseInt(startDiff /(1000*60*60*24*7))+1;
                break;
            } 
        }

        return week;
    };

    return obj;

});

[ 3 ]控制器用法(示例):

angular.module('app')

.controller('SampleController', function(DayService) {

  console.log(DayService.season(3));
  console.log(DayService.weekInSeason(3));

});

另一个注意事项:使用.run()检查是否Settings === null - 如果这是真的,您可以直接转到错误页面或显示问题的任何页面(这意味着应用程序引导但是其中一个请求的配置失败了。)

<强>更新

我检查了您提供的链接,似乎您使用的版本为AngularJS v1.0.8,其.catch()承诺实施中没有$q方法。

在解决此问题时,您需要考虑以下选项:

-1将您使用的AngularJS版本更改为最新的稳定版本1.2.23。 请注意,此选项可能会破坏一些高度依赖于您正在使用的版本的代码。

-2更改此块:

  $q.all(promises).then(function() {
    bootstrap(settings);
  }).catch(function() {
    bootstrap();
  });

为:

  $q.all(promises).then(function() {
    bootstrap(settings);
  }, function() {
    bootstrap();
  });

如果您已经拥有依赖于当前使用的AngularJS版本的现有代码,则此选项更安全但我建议您更改为当前的稳定版本,因为它具有比您当前使用的更多的设施和修复程序。 / p>

答案 1 :(得分:1)

使用您的范围和工厂关闭来存储对http调用的响应值。我创建了一个名为calData的对象,它恰好是一个承诺!

这使您能够在第一次调用工厂时通过运行IIFE(这是一个名为initService的函数)来解决一些问题,并且所有内容都链接在一起以在数据之后解析加载。

.factory('dayService', function dayServiceFactory($http, $q){
    var getCalData = $q.defer();
    var calData = gettingData.promise; // null/undefined until _loadData is called and resolved

    function _loadData(){
        var deferred = $q.defer();
        $http.get('content/calendar.json').success(function(data) {
            calData.seasons = data.calendar.seasons; // your code seems to always use at least calendar.seasons, so easier to assign that 
            deferred.resolve(data);
        });

        return deferred.promise;
    }

    // this function will automatically run and load data the first time the factory is executed
    (function initService(){
        _loadData().then(){
            // here is where you will build all your functions to assign properties to calData.seasons or any other child property of calData;

            calData.getSeason = function(){
                for (var i=0; i<data.calendar.seasons.season.length; i++){
                    // code here
                }
            }// function to get day using calData.seasons

            calData.weekInSeason = function(){}

            getCalData.resolve(); // this resolves the data in the outer scope
        }
    }());

    return calData; // returns the promise, and will execute the first time called

});

要在控制器中使用它,请确保在实例化控制器之前解析服务,或者使用控制器,在解析后使用数据分配。 (绑定值将在解决后自动更新)

 dayService.then(function(){
     // now you can use this:
     var week = dayService.weekInSeason();
  })

答案 2 :(得分:1)

您可以在getData方法中创建用于获取日历数据和链承诺的单独方法:

emmanuel.factory('DayService', ['$q', '$timeout', '$log',
  function($q, $timeout, $log) {
    return {
      season: season,
      weekInSeason: weekInSeason,
      getData: getData
    };

    function season(d) {
      $log.log('season called');

      return getCalendar(d).then(function(calendar) {
        return getSeason(d, calendar);
      });
    }

    function weekInSeason(d) {
      $log.log('weekInSeason called');

      return getCalendar(d).then(function(calendar) {
        return getWeekInSeason(d, calendar);
      });
    }

    function getData(d) {
      $log.log('getData called');

      return getCalendar(d).then(
        function(calendar) {
          return $q.all({
            season: getSeason(d, calendar),
            weekInSeason: getWeekInSeason(d, calendar)
          });
        }
      );
    }

    function getSeason(date, calendar) {
      $log.log('getSeason called');

      return {
        date: date,
        calendar: calendar,
        method: 'getSeason'
      };
    }

    function getWeekInSeason(date, calendar) {
      $log.log('getWeekInSeason called');

      return {
        date: date,
        calendar: calendar,
        method: 'getWeekInSeason'
      };
    }

    function getCalendar(d) {
      $log.log('getCalendar called');

      var deferred = $q.defer();
      $timeout(function() {
        deferred.resolve(12345);
      }, 2000);

      return deferred.promise;
    }
  }
]);

此外,如果在应用程序生命周期内calendar.json没有更改,您可以按照@runTarm的建议缓存calendar.json ajax请求结果

Plunker