我有一个服务:
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
,因此我也必须提供/它注入到service
。 所以,如果我这样做,像这样:
.service('StudentService', [ '$http', '$scope',
function ($http, $scope) {
我得到以下错误:
错误:[$注射器:unpr]未知提供商:$ scopeProvider < - $范围< - StudentService
错误(哇,这是整洁!)链接让我知道它是喷油器相关,并有可能与js文件中声明的顺序做。 我试图在重新排序他们index.html
,但我认为这是一些更简单,比如我在他们注射的方式。
采用了棱角分明的UI和角度-UI-路由器
Answer 1:
在$scope
,你看到被注入控制器是不是有些服务(如的注射材料的其余部分),而是一个范围对象。 许多范围的对象可以被创建(通常prototypically从父范围继承)。 所有范围的根是$rootScope
,您可以创建一个使用一个新的子范围的$new()
的任何范围的方法(包括$rootScope
)。
一个范围的目的是为了“粘合在一起”的介绍和您的应用程序的业务逻辑。 它没有多大意义,一个通$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); }
同样,见,这个简短的演示 。
LITTLE更新:
几句话,以避免在谈论使用服务,但与创造它可能出现的混乱service()
函数。
引述的文档$provide
:
角服务是由服务工厂创建一个单独的对象。 这些服务工厂是,这反过来,是由服务提供商创建功能。 该服务提供商构造函数。 当实例化必须包含一个名为属性$get
,它保存了服务工厂功能。
[...]
...在$provide
的服务有额外的辅助方法,而不指定提供商注册服务:
- 供应商(供应商) -注册到$喷射器服务提供商
- 常数(OBJ) -寄存器可以由提供者和服务进行访问的值/对象。
- 值(OBJ) -寄存器只能由服务,而不是供应商访问的值/对象。
- 工厂(FN) -注册服务工厂函数,FN,将被包裹在一个服务提供者对象,其$获得属性将包含给定的工厂函数。
- 服务(类) -注册一个构造函数,类将被包裹在一个服务提供者对象,其$获得属性将使用给定的构造函数实例化一个新的对象。
基本上,它说的是,每一个角服务使用注册$provide.provider()
但也有更简单的服务“快捷方式”的方法(其中两个是service()
和factory()
这一切都“归结”的服务,所以它不会让你使用哪种方法(只要为你服务的需求可以通过该方法被覆盖)太大的差别。
BTW, provider
VS service
VS factory
是对角的新来者最容易混淆的概念之一,但幸运的是有大量的资源(这里SO),使事情变得更容易。 (只是四处搜寻。)
(我希望清除它 - 让我知道,如果它没有。)
Answer 2:
不要试图修改$scope
内的服务,您可以实现$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
像这样:
this.students = $http.get(path).then(function (resp) {
return resp.data;
});
Answer 3:
好(长个)...如果你坚持有$scope
服务,你可以在里面访问:
创建的getter / setter服务
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');
}]);
Answer 4:
服务是单身,这是不符合逻辑的,以服务注入一个范围(这是确实的情况下,你不能注入在服务范围内)。 您可以将范围作为参数,但是这也是一个不好的设计选择,因为你将有范围在多个地方进行编辑,使其难以进行调试。 代码处理范围内的变量应该在控制器和服务呼叫转至服务。
Answer 5:
你可以让你的服务完全不知道的范围,但在你的控制器允许的范围进行异步更新。
您遇到的问题是,因为你不知道HTTP调用是异步的,这意味着你没有马上得到一个值,你可能。 例如,
var students = $http.get(path).then(function (resp) {
return resp.data;
}); // then() returns a promise object, not resp.data
有一个简单的方法来解决这个问题,它是提供一个回调函数。
.service('StudentService', [ '$http',
function ($http) {
// get some data via the $http
var path = '/students';
//save method create a new student if not already exists
//else update the existing object
this.save = function (student, doneCallback) {
$http.post(
path,
{
params: {
student: student
}
}
)
.then(function (resp) {
doneCallback(resp.data); // when the async http call is done, execute the callback
});
}
.controller('StudentSaveController', ['$scope', 'StudentService', function ($scope, StudentService) {
$scope.saveUser = function (user) {
StudentService.save(user, function (data) {
$scope.message = data; // I'm assuming data is a string error returned from your REST API
})
}
}]);
表格:
<div class="form-message">{{message}}</div>
<div ng-controller="StudentSaveController">
<form novalidate class="simple-form">
Name: <input type="text" ng-model="user.name" /><br />
E-mail: <input type="email" ng-model="user.email" /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<input type="button" ng-click="reset()" value="Reset" />
<input type="submit" ng-click="saveUser(user)" value="Save" />
</form>
</div>
这去掉了一些为简洁的业务逻辑,我还没有实际测试过的代码,但像这样的工作。 主要的概念是从控制器传递一个回调,向四周在今后后来被称为服务。 如果你熟悉这的NodeJS是同一个概念。
Answer 6:
钻进了同样的困境。 我结束了以下内容。 所以在这里我不是注射的范围对象进厂,但使用$ HTTP服务返回承诺的概念设置$范围在控制器中。
(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);
}());
Answer 7:
代码处理范围内的变量应该在控制器和服务呼叫转至服务。
你可以注入$rootScope
使用的目的$rootScope.$broadcast
和$rootScope.$on
。
否则,避免注射$rootScope
。 看到
- 常见缺陷:
$rootScope
存在,但它可用于邪恶 。
文章来源: Injecting $scope into an angular service function()