Angular - 从Factory方法检索数据的最佳实践

时间:2016-01-27 02:34:31

标签: javascript angularjs json factory

我正在寻找有关从本地JSON文件检索数据并处理响应的最佳方法的一些信息。在浏览Stack Overflow之后,我有一些不同的想法,因为我已经看到了多种做同样事情的方法(虽然没有解释为什么可能会优先考虑或不优选)。

基本上,我有一个Angular应用程序,它利用工厂从JSON文件中检索数据;然后我在我的html文件中使用它之前等待响应在我的控制器中解析,类似于下面的内容:

选项1

厂:

comparison.factory('Info', ['$http', function($http) {
var retrievalFile = 'retrievalFile.json';

return {
 retrieveInfo: function() {
  return $http.get(retrievalFile);
 }
}

}]);

控制器:

comparison.controller('comparisonController', ['$scope', 'Info', function($scope, Info) {

Info.retrieveInfo().then(function(response) {
  $scope.info = response.data;
});

}]);

我的主要观点是找出何时最好等待响应解决,或者甚至是否重要。我正在想让工厂恢复履行的承诺,并等待控制器也检索数据。在我看来,最好将所有数据检索抽象出控制器并进入工厂,但我不确定这是否延伸到等待工厂本身返回的实际数据。考虑到这一点,我对是否选择选项1或选项2感到困惑,并且非常感谢来自更有经验/合格开发人员的一些反馈!

选项2

厂:

comparison.factory('Info', ['$http', function($http) {
var retrievalFile = 'retrievalFile.json';

return {
  retrieveInfo: function() {
    return $http.get(retrievalFile).then(function(response) {
      return response.data;
    });
  }
}

}]);

控制器:

comparison.controller('comparisonController', ['$scope', 'Info', function($scope, Info) {

Info.retrieveInfo().then(function(response) {
  $scope.info = response;
});

}]);

提前感谢您的任何意见/建议!

4 个答案:

答案 0 :(得分:4)

这取决于您的控制器的期望以及您如何设置应用程序。一般来说,我总是选择第二种选择。因为我在所有api请求中通常都有全局错误或成功处理程序,并且我有共享api service。像下面的东西。

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

app.service('ApiService', ['$http', function($http) {
    var get = function(url, params) {
    $http.get(url, { params: params })
        .then(handleSuccess, handleError);
  };

  // handle your global errors here
  // implementation will vary based upon how you handle error
  var handleError = function(response) {
    return $q.reject(response);
  };

  // handle your success here
  // you can return response.data or response based upon what you want
  var handleSuccess = function(response) {
    return response.data;
  };
}]);

app.service('InfoService', ['ApiService', function(ApiService) {
    var retrieveInfo = function() {
    return ApiService.get(retrievalFile);

    /**
    // or return custom object that your controller is expecting
    return ApiService.get.then(function(data) {
      return new Person(data);
    });
    **//
  };

  // I prefer returning public functions this way
  // as I can just scroll down to the bottom of service 
  // to see all public functions at one place rather than
  // to scroll through the large file
  return { retrieveInfo: retrieveInfo };
}]);

app.controller('InfoController', ['InfoService', function(InfoService) {
  InfoService.retrieveInfo().then(function(info) {
    $scope.info = info;
  });
}])

或者,如果您使用的是路由器,则可以将数据解析到控制器中。 ngRouter和uiRouter都支持解析:

$stateProvider.state({
    name: 'info',
  url: '/info',
  controller: 'InfoController',
  template: 'some template',
  resolve: {
    // this injects a variable called info in your controller
    // with a resolved promise that you return here
    info: ['InfoService', function(InfoService) {
        return InfoService.retrieveInfo();
    }]
  }
});

// and your controller will be like
// much cleaner right
app.controller('InfoController', ['info', function(info) {
    $scope.info = info;
}]);

答案 1 :(得分:2)

这真的只是偏好。我喜欢用API来思考它。您想要公开的API是什么?您是否希望控制器接收整个响应,或者您希望控制器只获得响应所包含的数据?如果您只使用response.data,则选项2效果很好,因为除了您感兴趣的数据之外,您永远不必处理任何事情。

一个很好的例子就是我在我工作的地方写的应用程序。我们有两个应用程序:后端API和我们的前端Angular应用程序。我们在前端应用程序中创建了一个API包装器服务。在服务本身中,我们为任何已记录错误代码的API端点放置.catch(我们使用Swagger来记录和定义我们的API)。在.catch中,我们处理这些错误代码并返回正确的错误。当我们的控制器/指令消耗服务时,它们会获得更严格的数据集。如果发生错误,那么UI通常是安全的,只显示从包装器服务发送的错误消息,并且不必担心查看错误代码。

同样,对于成功的回复,我们会在选项2中执行大部分操作。在许多情况下,我们会将数据细化为实际应用中最不实用的内容。通过这种方式,我们在服务中保留了大量的数据流和格式,而应用程序的其余部分则要做的事情要少得多。例如,如果我们需要基于该数据创建一个对象,我们只需将对象返回到promise链,这样控制器就不会在整个地方做到这一点。

答案 2 :(得分:1)

我会选择第二选项,因为它的选项实际上大致相同。但是,当我们添加像Person假设的模型结构时,请看看。

comparison.factory('Info', ['$http', function($http) {
var retrievalFile = 'retrievalFile.json';

return {
  retrieveInfo: function() {
    return $http.get(retrievalFile).then(function(response) {
      //we will return a Person...
      var data = response.data;
      return new Person(data.name, data.age, data.gender);
    });
  }
}

}]);

这非常简单,但是如果你必须将更复杂的数据映射到对象模型中(你可以检索具有自己项目的人员列表等等),那么当事情变得更复杂时,你可能想要添加处理数据和模型之间映射的服务。那么你有另一项服务DataMapper(例子),如果你选择第一个选项,你必须将DataMapper注入你的控制器,你必须通过你的工厂提出你的请求,并用注入的服务。然后你可能会说,我应该在这里拥有所有这些代码吗? ......很可能没有。

这是一个假设的案例,重要的是你如何构建代码,不会以你不理解的方式构建代码。最后看看这个:https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)并研究有关此原则的更多信息,但重点是javascript。

答案 3 :(得分:1)

好问题。几点:

  1. 控制器应该以视图为中心而不是以数据为中心 想要从控制器中删除数据逻辑,而是让它集中注意力 关于业务逻辑。
  2. 模型(MVC中的M)是应用程序和的数据表示 将容纳数据逻辑。在Angular案例中,这将是一项服务 或正确指出的工厂类。为什么这样好 例如:

    2.1 AccountsController(可能注入了多个数据模型)

    2.1.1 UserModel  
    2.1.2 AuthModel  
    2.1.3 SubscriptionModel  
    2.1.4 SettingsModel
    
  3. 有很多方法可以采用数据模型方法,但我会说你的服务类应该是数据REST模型,即获取,存储,缓存,验证等等。我已经包含了一个基本的例子,但建议你调查一下JavaScript OOP将帮助您指出如何构建数据模型,集合等的正确方向。

    下面是一个管理数据的服务类示例。注意我没有测试过这段代码,但它应该给你一个开始。

    实施例

        (function () {
            'use strict';
    
            ArticleController.$inject = ['$scope', 'Article'];
            function ArticleController($scope, Article) {
                var vm = this,
                    getArticles = function () {
                        return Article.getArticles()
                            .then(function (result) {
                                if (result) {
                                    return vm.articles = result;
                                }
                            });
                    };
    
    
                vm.getArticles = getArticles;
                vm.articles = {};
                // OR replace vm.articles with $scope if you prefer e.g.
                $scope.articles = {};
    
                $scope.userNgClickToInit = function () {
                    vm.getArticles();
                };
    
                // OR an init on document ready
                // BUT to honest I would put all init logic in service class so all in calling is init in ctrl and model does the rest
                function initArticles() {
                    vm.getArticles();
    
                    // OR chain
                    vm.getArticles()
                        .then(getCategories); // doesn't here, just an example
    
                }
    
                initArticles();
            }
    
            ArticleModel.$inject = ['$scope', '$http', '$q'];
            function ArticleModel($scope, $http, $q) {
                var model = this,
                    URLS = {
                        FETCH: 'data/articles.json'
                    },
                    articles;
    
                function extract(result) {
                    return result.data;
                }
    
                function cacheArticles(result) {
                    articles = extract(result);
                    return articles;
                }
    
                function findArticle(id) {
                    return _.find(articles, function (article) {
                        return article.id === parseInt(id, 10);
                    })
                }
    
                model.getArticles = function () {
                    return (articles) ? $q.when(articles) : $http.get(URLS.FETCH).then(cacheArticles);
                };
    
                model.getArticleById = function (id) {
                    var deferred = $q.defer();
                    if (articles) {
                        deferred.resolve(findArticle(id))
                    } else {
                        model.getBookmarks().then(function () {
                            deferred.resolve(findArticle(id))
                        })
                    }
                    return deferred.promise;
                };
    
                model.createArticle = function (article) {
                    article.id = articles.length;
                    articles.push(article);
                };
    
                model.updateArticle = function (bookmark) {
                    var index = _.findIndex(articles, function (a) {
                        return a.id == article.id
                    });
    
                    articles[index] = article;
                };
    
                model.deleteArticle = function (article) {
                    _.remove(articles, function (a) {
                        return a.id == article.id;
                    });
                };
            }
    
            angular.module('app.article.model', [])
            .controller('ArticleController', ArticleController)
            .service('Article', ArticleModel);
    
        })()