developer tip

각도 서비스 function ()에 $ scope 삽입

optionbox 2020. 8. 9. 10:21
반응형

각도 서비스 function ()에 $ scope 삽입


서비스가 있습니다 :

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주입 방식과 같이 더 간단한 것 같습니다.

Angular-UI 및 Angular-UI-Router 사용


$scope당신이 컨트롤러에 주입되는 것을 볼 것으로는 (주 사용 재료의 나머지 부분 등) 일부 서비스 아니지만, 범위 객체입니다. 많은 범위 개체를 만들 수 있습니다 (일반적으로 부모 범위에서 상 속됨). 모든 범위의 루트는입니다. 모든 범위 $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); }

또한이 짧은 데모를 참조하십시오 .


약간의 업데이트 :

서비스 사용에 대해 이야기 할 때 발생할 수있는 혼동을 피하기 위해 몇 마디 말하지만 service()기능으로 만들지는 않습니다.

에 대한 문서$provide 인용 :

Angular 서비스서비스 팩토리에서 만든 단일 개체입니다 . 이러한 서비스 팩토리서비스 제공 업체에 의해 생성되는 기능입니다 . 서비스 제공자는 생성자 함수입니다. 인스턴스화되면 서비스 팩토리 기능 $get을 보유하는 라는 속성을 포함해야합니다 . [...] ... 서비스에는 공급자를 지정하지 않고 서비스를 등록하는 추가 도우미 메서드가 있습니다.

$provide

  • provider (provider) -$ injector에 서비스 제공자 등록
  • constant (obj) -공급자 및 서비스에서 액세스 할 수있는 값 / 객체를 등록합니다.
  • value (obj) -공급자가 아닌 서비스에서만 액세스 할 수있는 값 / 객체를 등록합니다.
  • factory (fn) -$ get 속성에 지정된 팩토리 함수가 포함되는 서비스 공급자 개체에 래핑 될 서비스 팩토리 함수 fn을 등록합니다.
  • service (class) -$ get 속성이 지정된 생성자 함수를 사용하여 새 개체를 인스턴스화하는 서비스 공급자 개체에 래핑 될 클래스 인 생성자 함수를 등록합니다.

Basically, what it says is that every Angular service is registered using $provide.provider(), but there are "shortcut" methods for simpler services (two of which are service() and factory()).
It all "boils down" to a service, so it doesn't make much difference which method you use (as long as the requirements for your service can be covered by that method).

BTW, provider vs service vs factory is one of the most confusing concepts for Angular new-comers, but fortunately there are plenty of resources (here on SO) to make things easier. (Just search around.)

(I hope that clears it up - let me know if it doesn't.)


Instead of trying to modify the $scope within the service, you can implement a $watch within your controller to watch a property on your service for changes and then update a property on the $scope. Here is an example you might try in a controller:

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

One thing to note is that within your service, in order for the students property to be visible, it needs to be on the Service object or this like so:

this.students = $http.get(path).then(function (resp) {
  return resp.data;
});

Well (a long one) ... if you insist to have $scope access inside a service, you can:

Create a getter/setter service

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

Inject it and store the controller scope in it

ngapp.controller('myCtrl', ['$scope', 'Scopes', function($scope, Scopes) {
  Scopes.store('myCtrl', $scope);
}]);

Now, get the scope inside another service

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

Services are singletons, and it is not logical for a scope to be injected in service (which is case indeed, you cannot inject scope in service). You can pass scope as a parameter, but that is also a bad design choice, because you would have scope being edited in multiple places, making it hard for debugging. Code for dealing with scope variables should go in controller, and service calls go to the service.


You could make your service completely unaware of the scope, but in your controller allow the scope to be updated asynchronously.

The problem you're having is because you're unaware that http calls are made asynchronously, which means you don't get a value immediately as you might. For instance,

var students = $http.get(path).then(function (resp) {
  return resp.data;
}); // then() returns a promise object, not resp.data

There's a simple way to get around this and it's to supply a callback function.

.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
    })
  }
}]);

The form:

<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>

This removed some of your business logic for brevity and I haven't actually tested the code, but something like this would work. The main concept is passing a callback from the controller to the service which gets called later in the future. If you're familiar with NodeJS this is the same concept.


Got into the same predicament. I ended up with the following. So here I am not injecting the scope object into the factory, but setting the $scope in the controller itself using the concept of promise returned by $http service.

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

Code for dealing with scope variables should go in controller, and service calls go to the service.

You can inject $rootScope for the purpose of using $rootScope.$broadcast and $rootScope.$on.

Otherwise avoid injecting $rootScope. See

참고URL : https://stackoverflow.com/questions/22898927/injecting-scope-into-an-angular-service-function

반응형