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


107

서비스가 있습니다 :

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

답변:


183

$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 속성이 지정된 생성자 함수를 사용하여 새 개체를 인스턴스화하는 서비스 공급자 개체에 래핑 될 클래스 인 생성자 함수를 등록합니다.

기본적으로 모든 Angular 서비스는를 사용하여 등록 $provide.provider()되지만 더 간단한 서비스를위한 "바로 가기"방법이 있습니다 (그 중 두 가지는 service()factory()).
이 모든 것이 서비스에 "보이기"때문에 어떤 방법을 사용하든 큰 차이가 없습니다 (서비스 요구 사항이 해당 방법으로 처리 될 수있는 한).

BTW, providervs servicevs factory는 Angular 신규 사용자에게 가장 혼란스러운 개념 중 하나이지만 다행스럽게도 작업을 더 쉽게 할 수있는 많은 리소스 (여기 SO)가 있습니다. (그냥 주변을 검색하십시오.)

(그게 해결되기를 바랍니다. 그렇지 않은 경우 알려주세요.)


1
하나의 질문. 서비스라고 말하지만 코드 예제는 공장을 사용합니다. 저는 공장, 서비스 및 공급자의 차이점을 이제 막 이해하기 시작했습니다. 서비스를 사용하고 있었기 때문에 공장과 함께가는 것이 최선의 선택인지 확인하고 싶습니다. 당신의 예에서 많은 것을 배웠습니다. 바이올린과 아주 명확한 설명에 감사드립니다.
chris Frisina 2014

3
@chrisFrisina : 약간의 설명과 함께 답변을 업데이트했습니다. 기본적으로, 당신이 사용하는 경우 많은 차이가되지 않습니다 service또는 factory당신이하고 유를 겁니다 - 각도 서비스 . 그냥 있는지 이해 만든다 어떻게 그것은 당신의 요구에 맞는 각각의 일을하고있는 경우.
gkalpak

좋은 포스트! 많은 도움이됩니다!
Oni1 2014

감사합니다! 여기에 유사한 문제에 대한 멋진 기사가 있습니다. stsc3000.github.io/blog/2013/10/26/…
Terafor

@ExpertSystem $scope.studentsajax 호출이 완료되지 않으면 비어 있을까요? 아니면 $scope.students이 코드 블록이 진행중인 경우 부분적으로 채워질까요? students.push(student);
Yc Zhang

18

$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속성이 표시 되려면 서비스 내에서 속성이 Service 개체에 this있어야합니다.

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

12

글쎄 (긴 것) ... 서비스 내에서 액세스 권한 을 주장 하는 경우 다음 을 수행 $scope할 수 있습니다.

게터 / 세터 서비스 만들기

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

스코프가 어떻게 파괴되고 있습니까?
JK.

9

서비스는 싱글 톤이며 범위가 서비스에 삽입되는 것은 논리적이지 않습니다 (실제로 서비스에 범위를 삽입 할 수 없음). 범위를 매개 변수로 전달할 수 있지만 이는 잘못된 디자인 선택입니다. 여러 위치에서 범위를 편집하여 디버깅하기 어렵 기 때문입니다. 범위 변수를 처리하기위한 코드는 컨트롤러에 있어야하고 서비스 호출은 서비스로 이동해야합니다.


나는 당신이 말하는 것을 이해합니다. 그러나 제 경우에는 컨트롤러가 많고 매우 유사한 $ watch 세트로 스코프를 구성하고 싶습니다. 어떻게 / 어디서 하시겠습니까? 현재 저는 실제로 $ watches를 설정하는 서비스에 매개 변수로 범위를 전달합니다.
moritz 2015 년

@moritz는 보조 지시문 (범위 : false가있는 것이므로 다른 지시문에 의해 정의 된 범위를 사용함)을 구현할 수 있으며이 지시문은 시계의 바인딩과 필요한 다른 모든 것을 만듭니다. 그런 식으로 이러한 시계를 정의해야하는 모든 위치에서 다른 지시문을 사용할 수 있습니다. 스코프를 서비스에 전달하는 것은 정말 끔찍하기 때문입니다. :) (저를 믿으세요, 제가 거기에 있었고 결국 벽에 머리를 부딪 혔습니다)
tfrascaroli

@ 티미 뉴트론이 스코프를 지나는 것보다 훨씬 더 좋은 소리를냅니다. 감사!
moritz

확실한. 나는 여전히 나 자신을 배우고 있고,이 특별한 문제는 내가 최근에 이런 특별한 방식으로 다루었 고, 그것은 나에게 매력처럼 작용했다.
tfrascaroli

3

서비스가 범위를 완전히 인식하지 못하도록 만들 수 있지만 컨트롤러에서 범위를 비동기 적으로 업데이트 할 수 있습니다.

당신이 겪고있는 문제는 당신이 http 호출이 비동기 적으로 이루어진다는 것을 모르기 때문에 당신이 할 수있는 것처럼 즉시 값을 얻지 못한다는 것을 의미합니다. 예를 들어

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

이 문제를 해결하는 간단한 방법이 있으며 콜백 함수를 제공하는 것입니다.

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

이것은 간결성을 위해 비즈니스 로직 중 일부를 제거했으며 실제로 코드를 테스트하지는 않았지만 이와 같은 것이 작동합니다. 주요 개념은 컨트롤러에서 나중에 호출되는 서비스로 콜백을 전달하는 것입니다. NodeJS에 익숙하다면 이것은 동일한 개념입니다.


이 방법은 권장되지 않습니다. Promise .then메서드의 콜백 이 안티 패턴 인 이유를 참조하십시오 .
georgeawg

0

같은 곤경에 빠졌습니다. 나는 다음과 같이 끝났다. 그래서 여기서는 범위 객체를 공장에 주입하는 것이 아니라 $ 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);    
}());

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.