Das $scope
, was Sie sehen, wie es in Controller injiziert wird, ist kein Dienst (wie der Rest des injizierbaren Materials), sondern ein Scope-Objekt. Es können viele Bereichsobjekte erstellt werden (normalerweise prototypisch von einem übergeordneten Bereich erben). Die Wurzel aller Bereiche ist der, $rootScope
und Sie können einen neuen untergeordneten Bereich mit der $new()
Methode eines beliebigen Bereichs (einschließlich des $rootScope
) erstellen .
Der Zweck eines Bereichs besteht darin, die Präsentation und die Geschäftslogik Ihrer App "zusammenzukleben". Es macht nicht viel Sinn, a zu bestehen$scope
an einen Dienst .
Services sind Singleton-Objekte, die (unter anderem) zum Teilen von Daten (z. B. zwischen mehreren Controllern) verwendet werden und im Allgemeinen wiederverwendbare Codeteile kapseln (da sie injiziert werden können und ihre "Services" in jedem Teil Ihrer App anbieten, der sie benötigt: Controller, Richtlinien, Filter, andere Dienste usw.).
Ich bin sicher, dass verschiedene Ansätze für Sie funktionieren würden. Eines ist das Folgende:
Da das StudentService
für den Umgang mit Studentendaten zuständig ist, können Sie StudentService
eine Reihe von Studenten behalten und es mit jedem "teilen" lassen, der interessiert sein könnte (z $scope
. B. Ihrem ). Dies ist umso sinnvoller, wenn andere Ansichten / Controller / Filter / Dienste Zugriff auf diese Informationen benötigen (wenn derzeit keine verfügbar sind, wundern Sie sich nicht, wenn sie bald auftauchen).
Jedes Mal, wenn ein neuer Schüler hinzugefügt wird (unter Verwendung der save()
Dienstmethode), wird das eigene Schülerarray des Dienstes aktualisiert, und jedes andere Objekt, das dieses Array gemeinsam nutzt , wird ebenfalls automatisch aktualisiert.
Basierend auf dem oben beschriebenen Ansatz könnte Ihr Code folgendermaßen aussehen:
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.
});
};
}
]);
Eine Sache, bei der Sie bei diesem Ansatz vorsichtig sein sollten, ist, das Array des Dienstes niemals neu zuzuweisen, da dann alle anderen Komponenten (z. B. Bereiche) weiterhin auf das ursprüngliche Array verweisen und Ihre App beschädigt wird.
ZB um das Array zu löschen in StudentService
:
/* DON'T DO THAT */
var clear = function () { students = []; }
/* DO THIS INSTEAD */
var clear = function () { students.splice(0, students.length); }
Siehe auch diese kurze Demo .
KLEINES UPDATE:
Ein paar Worte, um die Verwirrung zu vermeiden, die auftreten kann, wenn über die Verwendung eines Dienstes gesprochen wird, dieser jedoch nicht mit der service()
Funktion erstellt wird.
Zitieren der Dokumente auf$provide
:
Ein Angular- Service ist ein Singleton-Objekt, das von einer Service-Factory erstellt wurde . Diese Servicefabriken sind Funktionen, die wiederum von einem Service Provider erstellt werden . Die Dienstleister sind Konstruktorfunktionen. Wenn sie instanziiert werden, müssen sie eine aufgerufene Eigenschaft enthalten $get
, die die Service Factory- Funktion enthält.
[...]
... der $provide
Dienst verfügt über zusätzliche Hilfsmethoden zum Registrieren von Diensten ohne Angabe eines Anbieters:
- Provider (Provider) - registriert einen Service Provider beim $ Injector
- Konstante (obj) - registriert einen Wert / ein Objekt, auf das bzw. das Anbieter und Dienste zugreifen können.
- Wert (obj) - registriert einen Wert / ein Objekt, auf das nur von Diensten zugegriffen werden kann, nicht von Anbietern.
- Fabrik (fn) - registriert eine Service-Factory-Funktion, fn, die in ein Service-Provider-Objekt eingeschlossen wird, dessen $ get-Eigenschaft die angegebene Factory-Funktion enthält.
- service (class) - registriert eine Konstruktorfunktion, eine Klasse, die in ein Service Provider-Objekt eingeschlossen wird, dessen $ get-Eigenschaft ein neues Objekt mithilfe der angegebenen Konstruktorfunktion instanziiert.
Grundsätzlich heißt es, dass jeder Angular-Dienst mit registriert wird $provide.provider()
, es gibt jedoch Verknüpfungsmethoden für einfachere Dienste (zwei davon sind service()
und factory()
).
Alles läuft auf einen Service hinaus, sodass es keinen großen Unterschied macht, welche Methode Sie verwenden (solange die Anforderungen für Ihren Service durch diese Methode abgedeckt werden können).
Übrigens provider
vs service
vs.factory
ist eines der verwirrendsten Konzepte für Angular-Neulinge, aber zum Glück gibt es viele Ressourcen (hier auf SO), um die Dinge einfacher zu machen. (Suchen Sie einfach herum.)
(Ich hoffe, das klärt es auf - lass es mich wissen, wenn es nicht so ist.)
service
oderfactory
- Sie werden mit und Angular Service enden . Stellen Sie einfach sicher, dass Sie verstehen, wie jeder funktioniert und ob er Ihren Anforderungen entspricht.$scope.students
leer sein, wenn der Ajax-Aufruf nicht beendet ist? Oder wird$scope.students
es teilweise gefüllt, wenn dieser Codeblock in Bearbeitung ist?students.push(student);
Anstatt zu versuchen, das
$scope
innerhalb des Dienstes zu ändern , können Sie ein$watch
innerhalb Ihres Controllers implementieren , um eine Eigenschaft in Ihrem Dienst auf Änderungen zu überwachen und dann eine Eigenschaft auf dem zu aktualisieren$scope
. Hier ist ein Beispiel, das Sie in einem Controller versuchen könnten:Beachten Sie, dass sich die
students
Eigenschaft innerhalb Ihres Dienstes auf dem Dienstobjekt befinden muss, damit sie sichtbar ist, oderthis
so:quelle
Nun (eine lange) ... wenn Sie darauf bestehen ,
$scope
Zugang zu einem Dienst zu haben, können Sie:Erstellen Sie einen Getter / Setter-Service
Injizieren Sie es und speichern Sie den Controller-Bereich darin
Holen Sie sich jetzt den Bereich in einen anderen Dienst
quelle
Dienste sind Singletons, und es ist nicht logisch, dass ein Bereich in den Dienst eingefügt wird (was in der Tat der Fall ist, dass Sie den Bereich nicht in den Dienst einfügen können). Sie können den Bereich als Parameter übergeben, dies ist jedoch auch eine schlechte Wahl für das Design, da der Bereich an mehreren Stellen bearbeitet werden muss, was das Debuggen erschwert. Der Code für den Umgang mit Bereichsvariablen sollte im Controller gespeichert werden, und Dienstaufrufe werden an den Dienst gesendet.
quelle
Sie können dafür sorgen, dass Ihr Dienst den Bereich überhaupt nicht kennt, aber in Ihrem Controller kann der Bereich asynchron aktualisiert werden.
Das Problem besteht darin, dass Sie nicht wissen, dass http-Aufrufe asynchron ausgeführt werden, was bedeutet, dass Sie nicht sofort einen Wert erhalten, wie Sie es könnten. Zum Beispiel,
Es gibt eine einfache Möglichkeit, dies zu umgehen und eine Rückruffunktion bereitzustellen.
Die Form:
Dies hat der Kürze halber einige Ihrer Geschäftslogiken entfernt, und ich habe den Code noch nicht getestet, aber so etwas würde funktionieren. Das Hauptkonzept besteht darin, einen Rückruf von der Steuerung an den Dienst weiterzuleiten, der später in der Zukunft aufgerufen wird. Wenn Sie mit NodeJS vertraut sind, ist dies das gleiche Konzept.
quelle
.then
ein Anti-Muster sind .Bin in die gleiche Situation geraten. Am Ende hatte ich Folgendes. Hier injiziere ich das Scope-Objekt also nicht in die Factory, sondern setze den $ scope im Controller selbst unter Verwendung des vom $ http- Dienst zurückgegebenen Versprechenskonzepts .
quelle
Code für den Umgang mit Bereichsvariablen sollte in der Steuerung gespeichert werden, und Dienstaufrufe werden an den Dienst gesendet.
Sie können
$rootScope
zum Zweck der Verwendung von$rootScope.$broadcast
und injizieren$rootScope.$on
.Andernfalls vermeiden Sie das Injizieren
$rootScope
. Sehen$rootScope
gibt es, aber es kann für das Böse verwendet werden .quelle