私はサービスをしています:
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
は、(残りの注入可能なもののような)なんらかのサービスではありませんが、Scopeオブジェクトです。多くのスコープオブジェクトを作成することができます(通常はプロトタイプで親スコープから継承します)。すべてのスコープのルートは$rootScope
であり、任意のスコープ($rootScope
を含む)の$new()
メソッドを使用して新しい子スコープを作成できます。
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.
});
};
}
]);
この方法を使用するときに注意する必要があることの1つは、サービスの配列を再割り当てしないことです。その場合、他のコンポーネント(スコープなど)は依然として元の配列を参照しているため、アプリは壊れます。
例えば。 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) - サービスプロバイダオブジェクトにラップされるサービスファクトリ関数fnを登録します。$ getプロパティは与えられたファクトリを含みます。関数。
- service(class) - サービスプロバイダオブジェクトにラップされるコンストラクタ関数classを登録します。このクラスの$ getプロパティは、を使用して新しいオブジェクトをインスタンス化します。与えられたコンストラクタ関数.
基本的には、すべてのAngularサービスは$provide.provider()
を使用して登録されるということですが、より単純なサービスには「ショートカット」メソッドがあります(そのうちの2つはservice()
およびfactory()
)。
それはすべてサービスに「煮詰まる」ので、あなたがどの方法を使用するかはそれほど大きな違いはありません(あなたのサービスの要件がその方法によってカバーされることができる限り)。
ところで、provider
vs service
vs factory
は、Angular初心者にとって最も分かりにくい概念の1つですが、幸い、物事を容易にするために十分なリソース(ここではSO)があります。 (周りを検索してください)
(私はそれがそれを解決することを願っています - それがそうでないかどうか私に知らせてください。)
サービス内で$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;
});
よく(長い)... 主張するサービス内で$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');
}]);
サービスはシングルトンであり、スコープがサービスにインジェクトされるのは論理的ではありません(実際には、スコープをインサービスにインジェクトすることはできません)。スコープをパラメータとして渡すこともできますが、スコープを複数の場所で編集することになり、デバッグが困難になるため、これも設計上の選択としては不適切です。スコープ変数を扱うためのコードはコントローラに入れるべきです、そして、サービス呼び出しはサービスに行きます。
あなたはあなたのサービスをスコープに全く気付かないようにすることができます、しかしあなたのコントローラでスコープが非同期に更新されることを可能にします。
あなたが抱えている問題は、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 の概念を使用して $ scope をコントローラ自体に設定します。 $ 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);
}());
スコープ変数を扱うためのコードはコントローラに入れるべきです、そして、サービス呼び出しはサービスに行きます。
$rootScope
および$rootScope.$broadcast
を使用する目的で$rootScope.$on
を注入することができます。
それ以外の場合は$rootScope
を挿入しないでください。見る