Подтвердить что ты не робот

Angular - Лучшая практика для извлечения данных из метода Factory

Я ищу информацию о наилучшем способе получения данных из локального файла JSON и обработки ответа. После просмотра, у меня есть смешанные мысли, поскольку я видел несколько способов сделать одно и то же (хотя и не объяснение, почему можно или не может быть предпочтительным).

По сути, у меня есть приложение Angular, использующее factory для извлечения данных из файла JSON; Затем я жду ответа на мой контроллер, прежде чем использовать его в моем html файле, как показано ниже:

Вариант 1

Factory:

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

}]);

Мой главный спор - это выяснить, когда лучше подождать ответа, чтобы решить, или если это даже имеет значение. Я играю с идеей о том, чтобы factory вернуть выполняемое обещание и ждать, пока контроллер также извлечет данные. На мой взгляд, лучше всего отбросить все данные из контроллера и в factory, но я не уверен, что это продолжается до ожидания возврата фактических данных в самом factory. Имея это в виду, я смущен тем, следует ли выбирать вариант 1 или вариант 2 и действительно оценил бы некоторые отзывы от более опытных/квалифицированных разработчиков!

Вариант 2

Factory:

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

}]);

Благодарим вас за любые предложения/предложения заранее!

4b9b3361

Ответ 1

Это зависит от того, что ожидает ваш контроллер и как вы настроили приложение. Как правило, я всегда выбираю второй вариант. Это потому, что у меня обычно есть глобальные ошибки или обработчики успеха во всех запросах 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;
}]);

Ответ 2

Это действительно просто предпочтение. Мне нравится думать об этом с точки зрения API. Какой API вы хотите открыть? Вы хотите, чтобы ваш контроллер получил весь ответ или вы хотите, чтобы ваш контроллер просто имел данные, которые обертывает ответ? Если вы только собираетесь использовать response.data, тогда вариант 2 отлично работает, так как вам никогда не придется иметь дело ни с чем, кроме данных, которые вас интересуют.

Хорошим примером является приложение, которое мы только что писали, где я работаю. У нас есть два приложения: back-end API и наше front-end приложение Angular. Мы создали службу обертки API в интерфейсном приложении. В самой службе мы помещаем .catch для любой из конечных точек API, у которых есть документированные коды ошибок (мы использовали Swagger для документирования и определения нашего API). В этом .catch мы обрабатываем эти коды ошибок и возвращаем правильную ошибку. Когда наши контроллеры/директивы потребляют услугу, они получают гораздо более строгий набор данных. Если возникает ошибка, пользовательский интерфейс обычно безопасен, чтобы просто отображать сообщение об ошибке, отправленное из службы обертки, и ему не придется беспокоиться о просмотре кодов ошибок.

Аналогично для успешных ответов мы делаем многое из того, что вы делаете в варианте 2. Во многих случаях мы уточняем данные до минимально полезного в реальном приложении. Таким образом, мы сохраняем много данных, связанных с изменением данных и форматированием в сервисе, а остальная часть приложения намного меньше. Например, если нам нужно создать объект на основе этих данных, мы просто сделаем это взамен объекта к цепочке обещаний, чтобы контроллеры не делали этого повсюду.

Ответ 3

Я бы выбрал второй вариант, так как ваши варианты на самом деле в основном одинаковые. Но посмотрим, когда мы добавим модельную структуру, например, 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);
    });
  }
}

}]);

Ответ 4

Хороший вопрос. Несколько пунктов:
  • Контроллеры должны быть ориентированы на ориентацию по сравнению с центрами данных, поэтому вы хотите удалить логику данных из контроллера и, скорее, сосредоточиться по бизнес-логике.
  • Модели (M в MVC) представляют собой представление данных вашего приложения и будет размещаться логика данных. В случае Angular это будет услуга или factory, как вы по праву указали. Почему это хорошо для Пример:

    2.1 AccountsController (может содержать несколько моделей данных)

    2.1.1 UserModel  
    2.1.2 AuthModel  
    2.1.3 SubscriptionModel  
    2.1.4 SettingsModel
    

Существует множество способов подхода к подходу к модели данных, но я бы сказал, что ваш класс обслуживания должен быть моделью данных 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);

    })()