1. ホーム
  2. angularjs

[解決済み] スコープをアンギュラーサービスにインジェクトする function()

2022-09-26 07:07:12

質問

あるサービスがあります。

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 をサービスに渡すことはあまり意味がありません。

サービスは、データを共有したり (たとえば複数のコントローラ間で)、 一般に再利用可能なコードをカプセル化するために使用されるシングルトン オブジェクトです (コントローラ、ディレクティブ、フィルタ、他のサービスなど、 アプリケーションのどの部分にも注入してサービスを提供することができます)。

私は、さまざまなアプローチがあなたのために働くと確信しています。1つはこれです。

というのも 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); }

参照:こちらも ショート・デモ .


LITTLE UPDATEです。

サービスを使用することについて話しているときに生じるかもしれない混乱を避けるために、いくつかの単語を service() 関数で作成するのではありません。

を引用して のドキュメントを引用しています。 $provide :

アンギュラ サービス が作成するシングルトンオブジェクトです。 サービスファクトリ . これらは サービス工場 は関数であり、順番に サービスプロバイダ . そのため サービスプロバイダ はコンストラクタ関数です。インスタンス化されたとき、それらは $get と呼ばれるプロパティが必要です。 サービスファクトリ 関数を保持します。

[...]

...その $provide サービスには、プロバイダを指定せずにサービスを登録するためのヘルパーメソッドが追加されています。

  • provider(プロバイダ) - サービスプロバイダを $injector に登録します。
  • 定数(obj) - は、プロバイダやサービスからアクセス可能な値/オブジェクトを登録します。
  • 値(obj) - は、プロバイダではなく、サービスからのみアクセス可能な値/オブジェクトを登録します。
  • ファクトリー(fn) - は、サービスプロバイダオブジェクトにラップされる、サービスファクトリ関数fnを登録します。
  • サービス(クラス) - は、コンストラクタ関数、サービスプロバイダオブジェクトにラップされるクラス、その$getプロパティが与えられたコンストラクタ関数を使用して新しいオブジェクトをインスタンス化することを登録します。

基本的に、これはすべてのAngularサービスが $provide.provider() を使用しますが、よりシンプルなサービスのための "ショートカット"メソッドがあります(そのうちの2つは service()factory() ).

全てはサービスに帰結するので、どの方法を使っても(サービスの要件がその方法でカバーできる限り)大差はないのです。

ところで providerservicefactory はAngular初心者にとって最も混乱する概念の1つですが、幸いなことに、物事を簡単にするためのリソースが(ここSOに)たくさんあります(検索してみてください)。(検索してみてください)。

(これで解決することを願っています。解決しない場合は教えてください)