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

chr*_*ina 106 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;
          }
        }
      }
    };
Run Code Online (Sandbox Code Playgroud)

但是当我打电话时save(),我无法访问$scope,并得到ReferenceError: $scope is not defined.所以逻辑步骤(对我而言)是提供save()$scope,因此我还必须提供/注入它service.所以,如果我这样做:

  .service('StudentService', [ '$http', '$scope',
                      function ($http, $scope) {
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

错误:[$ injector:unpr]未知提供者:$ scopeProvider < - $ scope < - StudentService

错误中的链接(哇哇哇!)让我知道它与注入器有关,可能与js文件的声明顺序有关.我曾尝试重新排序它们index.html,但我认为它更简单,例如我注入它们的方式.

使用Angular-UI和Angular-UI-Router

gka*_*pak 182

$scope你看到被注入控制器是不是有些服务(如的注射材料的其余部分),而是一个范围对象.可以创建许多范围对象(通常是原型继承自父范围).所有范围的根都是$rootScope,您可以使用$new()任何范围的方法(包括$rootScope)创建新的子范围.

Scope的目的是将应用程序的表示和业务逻辑"粘合在一起".将a传递$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.
        });
      };
    }
]);
Run Code Online (Sandbox Code Playgroud)

使用这种方法时应该注意的一件事是永远不要重新分配服务的数组,因为任何其他组件(例如范围)仍将引用原始数组,您的应用程序将会中断.
例如,清除阵列StudentService:

/* DON'T DO THAT   */  
var clear = function () { students = []; }

/* DO THIS INSTEAD */  
var clear = function () { students.splice(0, students.length); }
Run Code Online (Sandbox Code Playgroud)

另见这个简短的演示.


小更新:

用几句话来避免在谈论使用服务时可能出现的混淆,而不是使用该service()功能创建它.

引用文档$provide:

Angular 服务是由服务工厂创建的单例对象.这些服务工厂是由服务提供商创建的功能.该服务提供商是构造函数.实例化时,它们必须包含一个名为的属性$get,该属性包含服务工厂函数.
[...]
... $provide服务有额外的帮助方法来注册服务而不指定提供者:

  • provider(provider) - 使用$ injector注册服务提供者
  • constant(obj) - 注册可由提供者和服务访问的值/对象.
  • value(obj) - 注册只能由服务而不是提供者访问的值/对象.
  • factory(fn) - 注册一个服务工厂函数fn,它将包装在一个服务提供者对象中,其$ get属性将包含给定的工厂函数.
  • service(class) - 注册一个构造函数,该类将包装在一个服务提供者对象中,其$ get属性将使用给定的构造函数实例化一个新对象.

基本上,它说的是每个Angular服务都是使用注册的$provide.provider(),但是有更简单服务的"快捷方式"(其中两个是service()factory()).
这一切都"归结为"服务,因此您使用哪种方法(只要该方法可以涵盖您的服务要求)并没有多大区别.

BTW,providervs servicevs factory是Angular新人最困惑的概念之一,但幸运的是有很多资源(这里是SO)让事情变得更容易.(只需搜索一下.)

(我希望清除它 - 如果没有,请告诉我.)

  • @chrisFrisina:用一点解释更新了答案.基本上,如果你使用`service`或`factory`它没有太大的区别 - 你将结束你和**Angular服务**.只要确保你理解**每个人如何工作以及它是否适合你的需要. (3认同)

Kei*_*ris 18

$scope您可以$watch在控制器中实现a,而不是尝试修改服务内部,以便在服务上查看属性以进行更改,然后更新属性$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;
                }
            });
        }());
    }]);
Run Code Online (Sandbox Code Playgroud)

需要注意的一点是,在您的服务中,为了使students属性可见,它需要在Service对象上,或者this像这样:

this.students = $http.get(path).then(function (resp) {
  return resp.data;
});
Run Code Online (Sandbox Code Playgroud)


Jon*_*ker 12

好吧(很长一段)...如果你坚持$scope在服务中访问,你可以:

创建一个getter/setter服务

ngapp.factory('Scopes', function (){
  var mem = {};
  return {
    store: function (key, value) { mem[key] = value; },
    get: function (key) { return mem[key]; }
  };
});
Run Code Online (Sandbox Code Playgroud)

注入它并将控制器范围存储在其中

ngapp.controller('myCtrl', ['$scope', 'Scopes', function($scope, Scopes) {
  Scopes.store('myCtrl', $scope);
}]);
Run Code Online (Sandbox Code Playgroud)

现在,将范围放在另一个服务中

ngapp.factory('getRoute', ['Scopes', '$http', function(Scopes, $http){
  // there you are
  var $scope = Scopes.get('myCtrl');
}]);
Run Code Online (Sandbox Code Playgroud)


Erm*_*vic 8

服务是单例,并且在服务中注入范围是不合逻辑的(事实上,您不能在服务中注入范围).您可以将范围作为参数传递,但这也是一个糟糕的设计选择,因为您将在多个位置编辑范围,这使得调试变得困难.处理范围变量的代码应该进入控制器,服务调用进入服务.