我有服务:
angular.module('cfd')
.service('StudentService', [ '$http',
function ($http) {
// get some data via the $http
var path = 'data/people/students.json';
var students = $http.get(path).then(function (resp) {
return resp.data;
});
//save method create a new student if not already exists
//else update the existing object
this.save = function (student) {
if (student.id == null) {
//if this is new student, add it in students array
$scope.students.push(student);
} else {
//for existing student, find this student using id
//and update it.
for (i in students) {
if (students[i].id == student.id) {
students[i] = student;
}
}
}
};
但是当我致电save()
时,我无法访问$scope
,并获得ReferenceError: $scope is not defined
。所以逻辑步骤(对我而言)是使用$scope
提供save(),因此我还必须提供/注入service
。所以,如果我这样做:
.service('StudentService', [ '$http', '$scope',
function ($http, $scope) {
我收到以下错误:
错误:[$ injector:unpr]未知提供者:$ scopeProvider< - $ scope< - StudentService
错误中的链接(哇哇哇!)让我知道它与注入器有关,可能与js文件的声明顺序有关。我尝试在index.html
重新排序它们,但我认为它更简单,例如我注入它们的方式。
使用Angular-UI和Angular-UI-Router
答案 0 :(得分:181)
您看到被注入控制器的$scope
不是某些服务(就像其他可注入的东西一样),而是一个Scope对象。可以创建许多范围对象(通常是原型继承自父范围)。所有范围的根都是$rootScope
,您可以使用任何范围的$new()
方法(包括$rootScope
)创建新的子范围。
Scope的目的是将应用程序的表示和业务逻辑“粘合在一起”。将$scope
传递给服务没有多大意义。
服务是用于共享数据(例如,在多个控制器之间)的单例对象,并且通常封装可重用的代码片段(因为它们可以在应用程序的任何需要它们的部分中注入并提供其“服务” :控制器,指令,过滤器,其他服务等)。
我相信,各种方法都适合你。一个是这个:
由于StudentService
负责处理学生数据,因此您可以让StudentService
保留一系列学生,让它与可能感兴趣的人“分享”(例如您的$scope
)。这更有意义,如果有其他视图/控制器/过滤器/服务需要访问该信息(如果现在没有,如果它们很快就会弹出,不要感到惊讶)。 />
每次添加新学生时(使用服务的save()
方法),服务自己的学生数组将被更新,并且共享该数组的每个其他对象也将自动更新。
根据上述方法,您的代码可能如下所示:
angular.
module('cfd', []).
factory('StudentService', ['$http', '$q', function ($http, $q) {
var path = 'data/people/students.json';
var students = [];
// In the real app, instead of just updating the students array
// (which will be probably already done from the controller)
// this method should send the student data to the server and
// wait for a response.
// This method returns a promise to emulate what would happen
// when actually communicating with the server.
var save = function (student) {
if (student.id === null) {
students.push(student);
} else {
for (var i = 0; i < students.length; i++) {
if (students[i].id === student.id) {
students[i] = student;
break;
}
}
}
return $q.resolve(student);
};
// Populate the students array with students from the server.
$http.get(path).then(function (response) {
response.data.forEach(function (student) {
students.push(student);
});
});
return {
students: students,
save: save
};
}]).
controller('someCtrl', ['$scope', 'StudentService',
function ($scope, StudentService) {
$scope.students = StudentService.students;
$scope.saveStudent = function (student) {
// Do some $scope-specific stuff...
// Do the actual saving using the StudentService.
// Once the operation is completed, the $scope's `students`
// array will be automatically updated, since it references
// the StudentService's `students` array.
StudentService.save(student).then(function () {
// Do some more $scope-specific stuff,
// e.g. show a notification.
}, function (err) {
// Handle the error.
});
};
}
]);
<子>
使用这种方法时应该注意的一件事是永远不要重新分配服务的数组,因为任何其他组件(例如范围)仍将引用原始数组,您的应用程序将会中断。
例如。清除StudentService
中的数组:
/* DON'T DO THAT */
var clear = function () { students = []; }
/* DO THIS INSTEAD */
var clear = function () { students.splice(0, students.length); }
另请参阅此 short demo 。
LITTLE UPDATE:
说几句话可以避免在讨论使用服务时可能出现的混淆,而不是使用service()
函数创建它。
引用 docs on $provide
:
Angular 服务是由服务工厂创建的单例对象。这些服务工厂是功能,而这些功能又由服务提供商创建。 服务提供商是构造函数。实例化时,它们必须包含名为
$get
的属性,该属性包含服务工厂功能。
[...]
...$provide
服务有其他帮助方法来注册服务而不指定提供者:
- 提供商(提供商) - 使用$ injector
注册服务提供商- constant(obj) - 注册可由提供者和服务访问的值/对象。
- value(obj) - 注册只能由服务而非提供者访问的值/对象。
- factory(fn) - 注册一个服务工厂函数fn,它将包装在一个服务提供者对象中,其$ get属性将包含给定的工厂函数。
- service(class) - 注册一个构造函数,该类将包装在服务提供者对象中,其$ get属性将使用给定的构造函数实例化一个新对象。
基本上,它说的是每个Angular服务都是使用$provide.provider()
注册的,但是有更简单服务的“快捷方式”方法(其中两个是service()
和factory()
)。
这一切都“归结为”服务,因此您使用哪种方法(只要该方法可以涵盖您的服务要求)并没有太大区别。
provider
vs service
vs factory
是Angular新人最困惑的概念之一,但幸运的是,有足够的资源(这里是SO)来制作东西更轻松。 (只需搜索一下。)
(我希望清除它 - 如果没有,请告诉我。)
答案 1 :(得分:17)
您可以在控制器中实现$scope
来监视服务中的属性以进行更改,然后更新{{1}上的属性,而不是尝试修改服务中的$watch
。 }。以下是您可以在控制器中尝试的示例:
$scope
需要注意的一点是,在您的服务中,为了使angular.module('cfd')
.controller('MyController', ['$scope', 'StudentService', function ($scope, StudentService) {
$scope.students = null;
(function () {
$scope.$watch(function () {
return StudentService.students;
}, function (newVal, oldVal) {
if ( newValue !== oldValue ) {
$scope.students = newVal;
}
});
}());
}]);
属性可见,它需要位于服务对象或students
上,如下所示:
this
答案 2 :(得分:12)
嗯(很长一段时间)...如果您 坚持 在服务中拥有$scope
权限,您可以:
ngapp.factory('Scopes', function (){
var mem = {};
return {
store: function (key, value) { mem[key] = value; },
get: function (key) { return mem[key]; }
};
});
ngapp.controller('myCtrl', ['$scope', 'Scopes', function($scope, Scopes) {
Scopes.store('myCtrl', $scope);
}]);
ngapp.factory('getRoute', ['Scopes', '$http', function(Scopes, $http){
// there you are
var $scope = Scopes.get('myCtrl');
}]);
答案 3 :(得分:8)
服务是单例,并且在服务中注入范围是不合逻辑的(事实上,您无法在服务中注入范围)。您可以将范围作为参数传递,但这也是一个糟糕的设计选择,因为您将在多个位置编辑范围,这使得调试变得困难。处理范围变量的代码应该进入控制器,服务调用进入服务。
答案 4 :(得分:0)
陷入同样的困境。我最终得到了以下内容。所以这里我不是将范围对象注入工厂,而是使用 $ http <返回承诺的概念在控制器本身中设置 $ scope 强>服务。
(function () {
getDataFactory = function ($http)
{
return {
callWebApi: function (reqData)
{
var dataTemp = {
Page: 1, Take: 10,
PropName: 'Id', SortOrder: 'Asc'
};
return $http({
method: 'GET',
url: '/api/PatientCategoryApi/PatCat',
params: dataTemp, // Parameters to pass to external service
headers: { 'Content-Type': 'application/Json' }
})
}
}
}
patientCategoryController = function ($scope, getDataFactory) {
alert('Hare');
var promise = getDataFactory.callWebApi('someDataToPass');
promise.then(
function successCallback(response) {
alert(JSON.stringify(response.data));
// Set this response data to scope to use it in UI
$scope.gridOptions.data = response.data.Collection;
}, function errorCallback(response) {
alert('Some problem while fetching data!!');
});
}
patientCategoryController.$inject = ['$scope', 'getDataFactory'];
getDataFactory.$inject = ['$http'];
angular.module('demoApp', []);
angular.module('demoApp').controller('patientCategoryController', patientCategoryController);
angular.module('demoApp').factory('getDataFactory', getDataFactory);
}());
答案 5 :(得分:0)
用于处理范围变量的代码应在控制器中,服务调用应在服务中。
您可以注入$rootScope
以用于使用$rootScope.$broadcast
和$rootScope.$on
。
否则,请避免注入$rootScope
。参见