将$ scope注入angular度服务函数()
我有一个服务:
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
。 所以合乎逻辑的步骤(对我来说)是为save()提供$scope
,因此我还必须提供/注入到service
。 所以如果我这样做:
.service('StudentService', [ '$http', '$scope', function ($http, $scope) {
我得到以下错误:
错误:[$ injector:unpr]未知提供者:$ scopeProvider < – $ scope < – StudentService
错误中的链接(哇,这是整洁!)让我知道这是注射器相关的,可能与js文件的声明的顺序。 我已经尝试在index.html
中对它们进行重新sorting,但是我认为这是更简单的事情,比如我注入它们的方式。
使用Angular-UI和Angular-UI-Router
你看到被注入控制器的$scope
不是一些服务(像其他的可注入东西),而是一个Scope对象。 许多范围对象可以创build(通常是从父范围原型inheritance)。 所有作用域的根都是$rootScope
,您可以使用任何作用域(包括$rootScope
)的$new()
方法创build一个新的子作用域。
范围的目的是将应用程序的表示和业务逻辑“粘合在一起”。 将$scope
传递给服务没有多大意义。
服务是用来共享数据(例如在几个控制器之间)的单例对象,通常封装可重复使用的代码片段(因为它们可以在应用程序的任何需要它们的部分注入并提供“服务”:控制器,指令,filter,其他服务等)。
我相信,各种方法将为你工作。 一个是这样的:
由于StudentService
负责处理学生数据,因此您可以让StudentService
保存一系列学生,并让其与任何可能感兴趣的人(例如您的$scope
)“共享”。 这更有意义,如果有其他视图/控制器/filter/服务需要访问该信息(如果没有任何权利,不要惊讶,如果他们很快就会出现)。
每次添加新学生时(使用服务的save()
方法),服务自己的学生数组将被更新,并且共享该数组的每个其他对象也将自动更新。
基于上述方法,您的代码可能如下所示:
angular.module('cfd', []) .factory('StudentService', ['$http', function ($http) { 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 */ 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; } } } }; /* Populate the students array with students from the server */ $http.get(path).success(function (data) { 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 StudentService.save(student); // The $scope's `students` array will be automatically updated // since it references the StudentService's `students` array // Do some more $scope-specific stuff, // eg show a notification }; } ]);
有一件事你应该小心使用这种方法是不要重新分配服务的数组,因为然后任何其他组件(例如范围)将仍然引用原始数组,您的应用程序将中断。
例如,要清除StudentService
的数组:
/* DON'T DO THAT */ var clear = function () { students = []; } /* DO THIS INSTEAD */ var clear = function () { students.splice(0, students.length); }
另请参阅此简短演示 。
小小的更新:
用几句话来避免在讨论使用服务时可能出现的混淆,而不是用service()
函数创build它。
引用$provide
的文档 :
Angular 服务是由服务工厂创build的单例对象。 这些服务工厂是由服务提供者创build的function。 服务提供者是构造函数。 实例化时,它们必须包含一个名为
$get
的属性,该属性包含服务工厂function。
[…]
$provide
服务有额外的帮助方法来注册服务,而不指定提供者:
- 提供者(provider) – 用$注入器注册一个服务提供者
- 常量(obj) – 注册供应商和服务可以访问的值/对象。
- value(obj) – 注册只能由服务访问的值/对象,而不是提供者。
- 工厂(fn) – 注册服务工厂函数fn,该函数将被包装在服务提供者对象中,其$ get属性将包含给定的工厂函数。
- service(class) – 注册一个构造函数,该类将包装在一个服务提供者对象中,其$ get属性将使用给定的构造函数实例化一个新对象。
基本上,它所说的是每个Angular服务都使用$provide.provider()
注册,但是对于更简单的服务(其中两个是service()
和factory()
)有“快捷方式”方法。
这一切都“归结”为服务,所以你使用哪种方法没有太大的区别(只要你的服务需求可以被这种方法所覆盖)。
顺便说一句, provider
vs service
vs factory
是Angular新手最容易混淆的概念之一,但幸运的是,有很多资源(在这里)让事情变得更简单。 (只要四处搜寻)
(我希望清除它 – 如果没有,请告诉我。)
不要试图修改服务中的$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.students = $http.get(path).then(function (resp) { return resp.data; });
那么(很长一段时间)…如果你坚持有一个服务内的$scope
访问,你可以:
创build一个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'); }]);
服务是单例的,而且在服务中注入一个范围是不合逻辑的(事实上,你不能在服务中注入范围)。 您可以将范围作为parameter passing,但这也是一个糟糕的deviseselect,因为您将在多个位置编辑范围,使得debugging变得困难。 处理范围variables的代码应该放在控制器中,服务调用将转到该服务。
你可以使你的服务完全不知道范围,但在你的控制器允许范围被asynchronous更新。
你遇到的问题是因为你不知道http调用是asynchronous的,这意味着你没有立即得到一个值。 例如,
var students = $http.get(path).then(function (resp) { return resp.data; }); // then() returns a promise object, not resp.data
有一个简单的方法来解决这个问题,它提供了一个callback函数。
.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>
为了简洁起见,这删除了一些业务逻辑,而且我没有真正testing过这些代码,但是像这样的东西是可行的。 主要的概念是将控制器的callback传递给将来要调用的服务。 如果你熟悉NodeJS,这是一个概念。
陷入了同样的困境。 我结束了以下。 所以在这里,我不是将范围对象注入工厂,而是使用$ http服务返回的promise的概念在控制器中设置$ 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); }());
- 试图根据常量在控制器中dynamic设置templateUrl
- `ui-router` $ stateParams与$ state.params
- 使用UI-Router设置页面标题
- Angular UI路由器中的$ state.transitionTo()和$ state.go()之间的区别
- UI路由器的条件ui意见?
- 在ui-routerparsing过程中应用加载微调器
- Ui-Router $ state.go()不刷新数据
- 如何查看在AngularJS / UI-Router中configuration了哪些状态?
- 如何使用$ state.go发送和检索参数toParams和$ stateParams?