将$ 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); }());