To $scope
, co widzisz, jest wstrzykiwane do kontrolerów, nie jest jakąś usługą (jak reszta rzeczy do wstrzykiwania), ale jest obiektem Scope. Można utworzyć wiele obiektów zakresu (zwykle prototypowo dziedzicząc z zakresu nadrzędnego). Podstawą wszystkich zakresów jest $rootScope
i można utworzyć nowy zakres podrzędny przy użyciu $new()
metody dowolnego zakresu (w tym $rootScope
).
Celem zakresu jest „sklejenie” prezentacji i logiki biznesowej aplikacji. Przekazywanie a $scope
do usługi nie ma większego sensu .
Usługi to pojedyncze obiekty używane (między innymi) do udostępniania danych (np. Między kilkoma kontrolerami) i generalnie hermetyzowania fragmentów kodu wielokrotnego użytku (ponieważ można je wstrzykiwać i oferować swoje „usługi” w dowolnej części aplikacji, która ich potrzebuje: kontrolery, dyrektywy, filtry, inne usługi itp.).
Jestem pewien, że sprawdzą się u Ciebie różne podejścia. Jedna z nich jest następująca:
ponieważ StudentService
jest odpowiedzialny za przetwarzanie danych uczniów, możesz StudentService
przechowywać ich tablicę i „dzielić się” nimi z każdym, kto może być zainteresowany (np. Twoim $scope
). Ma to jeszcze większy sens, jeśli istnieją inne widoki / kontrolery / filtry / usługi, które muszą mieć dostęp do tych informacji (jeśli nie ma ich teraz, nie zdziw się, jeśli zaczną się pojawiać wkrótce).
Za każdym razem, gdy dodawany jest nowy uczeń (przy użyciu metody usługi save()
), własna tablica uczniów usługi będzie aktualizowana, a każdy inny obiekt udostępniający tę tablicę również zostanie automatycznie zaktualizowany.
Bazując na podejściu opisanym powyżej, Twój kod mógłby wyglądać następująco:
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.
});
};
}
]);
Jedną rzeczą, na którą należy zachować ostrożność podczas korzystania z tego podejścia, jest to, aby nigdy nie ponownie przypisywać tablicy usługi, ponieważ wtedy wszelkie inne komponenty (np. Zakresy) będą nadal odwoływać się do oryginalnej tablicy, a aplikacja ulegnie awarii.
Np., Aby wyczyścić tablicę w StudentService
:
/* DON'T DO THAT */
var clear = function () { students = []; }
/* DO THIS INSTEAD */
var clear = function () { students.splice(0, students.length); }
Zobacz także to krótkie demo .
MAŁA AKTUALIZACJA:
Kilka słów, aby uniknąć nieporozumień, które mogą powstać podczas mówienia o korzystaniu z usługi, ale nie podczas tworzenia jej za pomocą service()
funkcji.
Cytując dokumenty na$provide
:
Kątowym usługa jest pojedyncza obiekt stworzony przez fabrykę usług . Te fabryki usług to funkcje, które z kolei są tworzone przez usługodawcę . Do usługodawcy są funkcje konstruktora. Po utworzeniu muszą zawierać właściwość nazwaną $get
, która przechowuje funkcję fabryki usług .
[...]
... $provide
usługa posiada dodatkowe pomocnicze metody rejestracji usług bez określania dostawcy:
- provider (provider) - rejestruje usługodawcę w $ injectorze
- stała (obj) - rejestruje wartość / obiekt, do którego mają dostęp dostawcy i usługi.
- wartość (obj) - rejestruje wartość / obiekt, do którego dostęp mają tylko usługi, a nie dostawcy.
- factory (fn) - rejestruje funkcję fabryki usług, fn, która zostanie opakowana w obiekt dostawcy usług, którego właściwość $ get będzie zawierać daną funkcję fabryki.
- service (class) - rejestruje funkcję konstruktora, klasę, która zostanie opakowana w obiekt dostawcy usług, której właściwość $ get utworzy instancję nowego obiektu przy użyciu podanej funkcji konstruktora.
Zasadniczo mówi się, że każda usługa Angular jest rejestrowana przy użyciu $provide.provider()
, ale istnieją metody „skrótów” dla prostszych usług (z których dwie to service()
i factory()
).
To wszystko „sprowadza się” do usługi, więc nie ma większego znaczenia, której metody użyjesz (o ile wymagania dotyczące Twojej usługi mogą być objęte tą metodą).
BTW, provider
vs service
vs factory
to jedna z najbardziej zagmatwanych koncepcji dla nowicjuszy Angulara, ale na szczęście istnieje wiele zasobów (tutaj na SO), które ułatwiają życie. (Po prostu poszukaj.)
(Mam nadzieję, że to wyjaśnia sprawę - daj mi znać, jeśli nie.)
service
lubfactory
- skończysz z usługą Angular . Po prostu upewnij się, że rozumiesz, jak każdy z nich działa i czy odpowiada Twoim potrzebom.$scope.students
będzie puste, jeśli wywołanie AJAX nie zostanie zakończone? A może$scope.students
będzie częściowo zapełniony, jeśli ten blok kodu jest w toku?students.push(student);
Zamiast próbować modyfikować
$scope
w usłudze, możesz zaimplementować$watch
w kontrolerze, aby obserwować właściwość w usłudze pod kątem zmian, a następnie zaktualizować właściwość w$scope
. Oto przykład, który możesz wypróbować w kontrolerze:Należy zwrócić uwagę, że w ramach Twojej usługi, aby
students
nieruchomość była widoczna, musi znajdować się w obiekcie usługi lub wthis
podobny sposób:źródło
Cóż (długi) ... jeśli nalegasz na
$scope
dostęp do usługi, możesz:Utwórz usługę pobierającą / ustawiającą
Wstrzyknij go i zapisz w nim zakres kontrolera
Teraz uzyskaj zakres w innej usłudze
źródło
Usługi są pojedynczymi usługami i nie jest logiczne, aby zakres był wstrzykiwany do usługi (w rzeczywistości nie można wstrzyknąć zakresu do usługi). Możesz przekazać zakres jako parametr, ale jest to również zły wybór projektowy, ponieważ zakres byłby edytowany w wielu miejscach, co utrudniłoby debugowanie. Kod do obsługi zmiennych zakresu powinien trafiać do kontrolera, a wywołania usługi - do usługi.
źródło
Możesz sprawić, że usługa będzie całkowicie nieświadoma zakresu, ale w kontrolerze zezwalaj na asynchroniczne aktualizowanie zakresu.
Problem polega na tym, że nie jesteś świadomy tego, że wywołania http są wykonywane asynchronicznie, co oznacza, że nie otrzymujesz wartości od razu, tak jak możesz. Na przykład,
Istnieje prosty sposób obejścia tego problemu i polega na dostarczeniu funkcji zwrotnej.
Formularz:
To usunęło część logiki biznesowej dla zwięzłości i tak naprawdę nie testowałem kodu, ale coś takiego zadziała. Główną koncepcją jest przekazanie wywołania zwrotnego ze sterownika do usługi, która zostanie wywołana później w przyszłości. Jeśli znasz NodeJS, to jest ta sama koncepcja.
źródło
.then
metod Promise są anty-wzorcem .Wpadłem w tę samą sytuację. Skończyło się na tym. Więc tutaj nie wstrzykuję obiektu scope do fabryki, ale ustawiam $ scope w samym kontrolerze używając koncepcji obietnicy zwracanej przez usługę $ http .
źródło
Kod do obsługi zmiennych zakresu powinien trafiać do kontrolera, a wywołania usługi - do usługi.
Możesz wstrzyknąć
$rootScope
w celu użycia$rootScope.$broadcast
i$rootScope.$on
.W przeciwnym razie unikaj wstrzyknięcia
$rootScope
. Widzieć$rootScope
istnieje, ale można go użyć do zła .źródło