AngularJS: 具有异步请求的多个依赖服务
AngularJS: Multiple depending services with asynchronous requests
我想构建一个 angular 应用程序,主要用作显示数据,但以稍微动态的方式。我有多个 JSON 文件,结构相同,但内容不同,每种语言一个:
res/information_en.json
[
{
"name": "Jobs",
"data": [
{
"title": "Title",
"employer": "Employer",
"description": "Description",
"begin": "2015-12",
"end": "2016-12"
}
]
},{
"name": "Personal",
"data": [
{
"firstname": "Christian",
"lastname": "Steinmeyer"
}
]
}
]
德语版:
res/information_de.json
[
{
"name": "Jobs",
"data": [
{
"title": "Titel",
"employer": "Arbeitgeber",
"description": "Beschreibung",
"begin": "2015-12",
"end": "2016-12"
}
]
},{
"name": "Personal",
"data": [
{
"firstname": "Christian",
"lastname": "Steinmeyer"
}
]
}
]
此外,我还有另一个 JSON 文件,用于跟踪所有语言:
res/languages.json
[
{
"name": "English",
"short": "en",
"active": true
},{
"name": "Deutsch",
"short": "de",
"active": false
}
]
本质上,我想要的是让用户能够选择语言,信息应该显示在 res/languages.json
提供的可用语言中。为此,我创建了第一个服务:
app/services/language-service.js
(function(){
'use strict';
angular.module('gulpAngularCv').factory('LanguageService', LanguageService);
/** @ngInject */
function LanguageService($log, $q, $resource, toastr) {
var service = {};
service.getLanguages = getLanguages;
service.select = select;
service.getActiveLanguage = getActiveLanguage;
var initialized = false;
var languages = [];
function getLanguages(){
if (initialized){
return languages;
} else {
initialize().then(
function success(result){
angular.forEach(result, function addLanguage(language){
languages.push(language);
})
initialized = true;
}, function fail(reject){
$log.error("Loading 'res/languages.json' failed.");
$log.error(reject);
toastr.error('Make sure, it is formatted correctly.', 'Loading language file failed!');
}
);
return languages;
}
}
function initialize(){
var deferred = $q.defer();
$resource('res/languages.json').query(
function success(result){
deferred.resolve(result);
}, function fail(reject){
deferred.reject(reject);
}
);
return deferred.promise;
}
function select(language){
// iterate over all languages
// deactivate, if active and activate if equal to parameter
}
function getActiveLanguage(){
for (var i = 0; i < languages.length; i++){
if (languages[i].active){
return languages[i];
}
}
}
return service;
}
})();
当从控制器调用时,这本身就像一个魅力。但正如我所说,我也希望能够从相应的 json 文件中加载信息。我尝试使用下一个服务:
app/services/information-service.js
(function(){
'use strict';
angular.module('gulpAngularCv').factory('InformationService', InformationService);
/** @ngInject */
function InformationService($log, $q, $resource, toastr, LanguageService) {
var service = {};
service.getInformation = getInformation;
var initialized = {};
var information = [];
function getInformation(){
var language = LanguageService.getActiveLanguage();
if (initialized === language){
return information;
} else {
initialize(language).then(
function success(result){
angular.forEach(result, function addInformation(information){
information.push(information);
})
initialized = language;
}, function fail(reject){
$log.error("Loading 'res/information_" + language.short + ".json' failed.");
$log.error(reject);
toastr.error('Make sure, it is formatted correctly.', 'Loading information file failed!');
}
);
return information;
}
}
function initialize(language){
var deferred = $q.defer();
$resource("res/information_" + language.short + ".json").query(
function success(result){
deferred.resolve(result);
}, function fail(reject){
deferred.reject(reject);
}
);
return deferred.promise;
}
return service;
}
})();
我,基本上,做同样的事情,但这次,它不会工作,因为看起来,这个服务被首先注入,即使它依赖于另一个。我在浏览器的控制台中收到以下错误:
TypeError: Cannot read property 'short' of undefined
at initialize (http://localhost:3000/app/services/information-service.js:44:48)
at Object.getInformation (http://localhost:3000/app/services/information-service.js:25:13)
at new MainController (http://localhost:3000/app/main/main-controller.js:12:40)
at invoke (http://localhost:3000/bower_components/angular/angular.js:4535:17)
at Object.instantiate (http://localhost:3000/bower_components/angular/angular.js:4543:27)
at http://localhost:3000/bower_components/angular/angular.js:9395:28
at link (http://localhost:3000/bower_components/angular-route/angular-route.js:977:26)
at invokeLinkFn (http://localhost:3000/bower_components/angular/angular.js:9039:9)
at nodeLinkFn (http://localhost:3000/bower_components/angular/angular.js:8533:11)
at compositeLinkFn (http://localhost:3000/bower_components/angular/angular.js:7929:13) <div ng-view="" class="ng-scope">
在我看来,这个错误很奇怪,因为在调用时,按照我实现的方式,承诺的应该已经解决了。
为了完整起见,这里的MainController也是:
app/main/main-controller.js
(function() {
'use strict';
angular
.module('gulpAngularCv')
.controller('MainController', MainController);
/** @ngInject */
function MainController(InformationService) {
var vm = this;
vm.categories = InformationService.getInformation();
}
})();
我已经查看了 this and this 问题以及官方文档,但到目前为止他们只让我了解了...
毕竟,我认为我的问题的原因是 Javascript 中的可见性和范围。在信息服务中(在问题中发布),我使用了一个名为 information
的 "global" 变量,但是在 getInformation()
方法的每个循环的 angular 中,我创建一个具有相同名称的局部变量,这样就不会像我打算的那样将任何内容添加到我的原始变量中。为了完成,我将再次添加这两个服务的最终实现。请注意,我不仅解决了该错误,而且还对信息服务进行了一些重构(如下)。此解决方案如我所愿。
app/services/language-service.js
(function(){
'use strict';
angular.module('gulpAngularCv').factory('LanguageService', LanguageService);
/** @ngInject */
function LanguageService($log, $q, $resource, toastr) {
var service = {};
service.getLanguages = getLanguages;
service.select = select;
service.getActiveLanguage = getActiveLanguage;
var initialized = false;
var languages = [];
function getLanguages(){
if (initialized){
return languages;
} else {
initialize().then(
function success(result){
$log.debug("Loaded languages from 'res/languages.json'");
$log.debug(result);
angular.forEach(result, function addLanguage(language){
$log.debug(language);
languages.push(language);
})
initialized = true;
}, function fail(reject){
$log.error("Loading 'res/languages.json' failed.");
$log.error(reject);
toastr.error('Make sure, it is formatted correctly.', 'Loading language file failed!');
}
);
return languages;
}
}
function initialize(){
var deferred = $q.defer();
$resource('res/languages.json').query(
function success(result){
deferred.resolve(result);
}, function fail(reject){
deferred.reject(reject);
}
);
return deferred.promise;
}
function select(language){
// iterate over all languages
// deactivate, if active and activate if equal to parameter
}
function getActiveLanguage(){
for (var i = 0; i < languages.length; i++){
if (languages[i].active){
return languages[i];
}
}
}
return service;
}
})();
和信息服务:
app/services/information-service.js
(function(){
'use strict';
angular.module('gulpAngularCv').factory('InformationService', InformationService);
/** @ngInject */
function InformationService($log, $q, $resource, $rootScope, toastr, LanguageService) {
var service = {};
service.getCategories = getCategories;
var model = {};
model.initialized = null;
model.categories = [];
function getCategories(){
$log.debug("getCategories");
loadData();
return model.categories;
}
function loadData(){
var language = LanguageService.getActiveLanguage();
if (language && model.initialized !== language){
initialize(language).then(
function success(data){
model.categories.length = 0;
angular.forEach(data.categories, function addInformation(datum){
$log.debug(datum);
model.categories.push(datum);
})
}, function fail(reject){
toastr.error('Make sure, it is formatted correctly.', 'Loading information file failed!');
}
);
}
}
function initialize(language){
var deferred = $q.defer();
$resource("res/information_" + language.short + ".json").get(
function success(result){
deferred.resolve(result);
model.initialized = language;
}, function fail(reject){
deferred.reject(reject);
}
);
return deferred.promise;
}
return service;
}
})();
我想构建一个 angular 应用程序,主要用作显示数据,但以稍微动态的方式。我有多个 JSON 文件,结构相同,但内容不同,每种语言一个:
res/information_en.json
[
{
"name": "Jobs",
"data": [
{
"title": "Title",
"employer": "Employer",
"description": "Description",
"begin": "2015-12",
"end": "2016-12"
}
]
},{
"name": "Personal",
"data": [
{
"firstname": "Christian",
"lastname": "Steinmeyer"
}
]
}
]
德语版:
res/information_de.json
[
{
"name": "Jobs",
"data": [
{
"title": "Titel",
"employer": "Arbeitgeber",
"description": "Beschreibung",
"begin": "2015-12",
"end": "2016-12"
}
]
},{
"name": "Personal",
"data": [
{
"firstname": "Christian",
"lastname": "Steinmeyer"
}
]
}
]
此外,我还有另一个 JSON 文件,用于跟踪所有语言:
res/languages.json
[
{
"name": "English",
"short": "en",
"active": true
},{
"name": "Deutsch",
"short": "de",
"active": false
}
]
本质上,我想要的是让用户能够选择语言,信息应该显示在 res/languages.json
提供的可用语言中。为此,我创建了第一个服务:
app/services/language-service.js
(function(){
'use strict';
angular.module('gulpAngularCv').factory('LanguageService', LanguageService);
/** @ngInject */
function LanguageService($log, $q, $resource, toastr) {
var service = {};
service.getLanguages = getLanguages;
service.select = select;
service.getActiveLanguage = getActiveLanguage;
var initialized = false;
var languages = [];
function getLanguages(){
if (initialized){
return languages;
} else {
initialize().then(
function success(result){
angular.forEach(result, function addLanguage(language){
languages.push(language);
})
initialized = true;
}, function fail(reject){
$log.error("Loading 'res/languages.json' failed.");
$log.error(reject);
toastr.error('Make sure, it is formatted correctly.', 'Loading language file failed!');
}
);
return languages;
}
}
function initialize(){
var deferred = $q.defer();
$resource('res/languages.json').query(
function success(result){
deferred.resolve(result);
}, function fail(reject){
deferred.reject(reject);
}
);
return deferred.promise;
}
function select(language){
// iterate over all languages
// deactivate, if active and activate if equal to parameter
}
function getActiveLanguage(){
for (var i = 0; i < languages.length; i++){
if (languages[i].active){
return languages[i];
}
}
}
return service;
}
})();
当从控制器调用时,这本身就像一个魅力。但正如我所说,我也希望能够从相应的 json 文件中加载信息。我尝试使用下一个服务:
app/services/information-service.js
(function(){
'use strict';
angular.module('gulpAngularCv').factory('InformationService', InformationService);
/** @ngInject */
function InformationService($log, $q, $resource, toastr, LanguageService) {
var service = {};
service.getInformation = getInformation;
var initialized = {};
var information = [];
function getInformation(){
var language = LanguageService.getActiveLanguage();
if (initialized === language){
return information;
} else {
initialize(language).then(
function success(result){
angular.forEach(result, function addInformation(information){
information.push(information);
})
initialized = language;
}, function fail(reject){
$log.error("Loading 'res/information_" + language.short + ".json' failed.");
$log.error(reject);
toastr.error('Make sure, it is formatted correctly.', 'Loading information file failed!');
}
);
return information;
}
}
function initialize(language){
var deferred = $q.defer();
$resource("res/information_" + language.short + ".json").query(
function success(result){
deferred.resolve(result);
}, function fail(reject){
deferred.reject(reject);
}
);
return deferred.promise;
}
return service;
}
})();
我,基本上,做同样的事情,但这次,它不会工作,因为看起来,这个服务被首先注入,即使它依赖于另一个。我在浏览器的控制台中收到以下错误:
TypeError: Cannot read property 'short' of undefined
at initialize (http://localhost:3000/app/services/information-service.js:44:48)
at Object.getInformation (http://localhost:3000/app/services/information-service.js:25:13)
at new MainController (http://localhost:3000/app/main/main-controller.js:12:40)
at invoke (http://localhost:3000/bower_components/angular/angular.js:4535:17)
at Object.instantiate (http://localhost:3000/bower_components/angular/angular.js:4543:27)
at http://localhost:3000/bower_components/angular/angular.js:9395:28
at link (http://localhost:3000/bower_components/angular-route/angular-route.js:977:26)
at invokeLinkFn (http://localhost:3000/bower_components/angular/angular.js:9039:9)
at nodeLinkFn (http://localhost:3000/bower_components/angular/angular.js:8533:11)
at compositeLinkFn (http://localhost:3000/bower_components/angular/angular.js:7929:13) <div ng-view="" class="ng-scope">
在我看来,这个错误很奇怪,因为在调用时,按照我实现的方式,承诺的应该已经解决了。
为了完整起见,这里的MainController也是:
app/main/main-controller.js
(function() {
'use strict';
angular
.module('gulpAngularCv')
.controller('MainController', MainController);
/** @ngInject */
function MainController(InformationService) {
var vm = this;
vm.categories = InformationService.getInformation();
}
})();
我已经查看了 this and this 问题以及官方文档,但到目前为止他们只让我了解了...
毕竟,我认为我的问题的原因是 Javascript 中的可见性和范围。在信息服务中(在问题中发布),我使用了一个名为 information
的 "global" 变量,但是在 getInformation()
方法的每个循环的 angular 中,我创建一个具有相同名称的局部变量,这样就不会像我打算的那样将任何内容添加到我的原始变量中。为了完成,我将再次添加这两个服务的最终实现。请注意,我不仅解决了该错误,而且还对信息服务进行了一些重构(如下)。此解决方案如我所愿。
app/services/language-service.js
(function(){
'use strict';
angular.module('gulpAngularCv').factory('LanguageService', LanguageService);
/** @ngInject */
function LanguageService($log, $q, $resource, toastr) {
var service = {};
service.getLanguages = getLanguages;
service.select = select;
service.getActiveLanguage = getActiveLanguage;
var initialized = false;
var languages = [];
function getLanguages(){
if (initialized){
return languages;
} else {
initialize().then(
function success(result){
$log.debug("Loaded languages from 'res/languages.json'");
$log.debug(result);
angular.forEach(result, function addLanguage(language){
$log.debug(language);
languages.push(language);
})
initialized = true;
}, function fail(reject){
$log.error("Loading 'res/languages.json' failed.");
$log.error(reject);
toastr.error('Make sure, it is formatted correctly.', 'Loading language file failed!');
}
);
return languages;
}
}
function initialize(){
var deferred = $q.defer();
$resource('res/languages.json').query(
function success(result){
deferred.resolve(result);
}, function fail(reject){
deferred.reject(reject);
}
);
return deferred.promise;
}
function select(language){
// iterate over all languages
// deactivate, if active and activate if equal to parameter
}
function getActiveLanguage(){
for (var i = 0; i < languages.length; i++){
if (languages[i].active){
return languages[i];
}
}
}
return service;
}
})();
和信息服务:
app/services/information-service.js
(function(){
'use strict';
angular.module('gulpAngularCv').factory('InformationService', InformationService);
/** @ngInject */
function InformationService($log, $q, $resource, $rootScope, toastr, LanguageService) {
var service = {};
service.getCategories = getCategories;
var model = {};
model.initialized = null;
model.categories = [];
function getCategories(){
$log.debug("getCategories");
loadData();
return model.categories;
}
function loadData(){
var language = LanguageService.getActiveLanguage();
if (language && model.initialized !== language){
initialize(language).then(
function success(data){
model.categories.length = 0;
angular.forEach(data.categories, function addInformation(datum){
$log.debug(datum);
model.categories.push(datum);
})
}, function fail(reject){
toastr.error('Make sure, it is formatted correctly.', 'Loading information file failed!');
}
);
}
}
function initialize(language){
var deferred = $q.defer();
$resource("res/information_" + language.short + ".json").get(
function success(result){
deferred.resolve(result);
model.initialized = language;
}, function fail(reject){
deferred.reject(reject);
}
);
return deferred.promise;
}
return service;
}
})();