将$ scope注入角度服务函数()

时间:2014-04-06 19:33:17

标签: angularjs angular-ui angular-ui-router

我有服务:

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

6 个答案:

答案 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())。
这一切都“归结为”服务,因此您使用哪种方法(只要该方法可以涵盖您的服务要求)并没有太大区别。

BTW,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权限,您可以:

创建一个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');
}]);

答案 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。参见