Lo $scope
que ves que se inyecta en los controladores no es un servicio (como el resto de las cosas inyectables), sino un objeto Scope. Se pueden crear muchos objetos de alcance (generalmente heredando prototípicamente de un alcance principal). La raíz de todos los ámbitos es $rootScope
y puede crear un nuevo ámbito secundario utilizando el $new()
método de cualquier ámbito (incluido $rootScope
).
El propósito de un alcance es "unir" la presentación y la lógica empresarial de su aplicación. No tiene mucho sentido $scope
convertir un en un servicio.
Los servicios son objetos singleton que se utilizan (entre otras cosas) para compartir datos (por ejemplo, entre varios controladores) y generalmente encapsulan piezas de código reutilizables (ya que pueden inyectarse y ofrecer sus "servicios" en cualquier parte de su aplicación que los necesite: controladores, directivas, filtros, otros servicios, etc.).
Estoy seguro de que varios enfoques funcionarían para usted. Una es la siguiente:
dado que StudentService
está a cargo de manejar los datos de los estudiantes, puede StudentService
mantener una matriz de estudiantes y dejar que la "comparta" con quien pueda estar interesado (por ejemplo, su $scope
). Esto tiene aún más sentido, si hay otras vistas / controladores / filtros / servicios que necesitan tener acceso a esa información (si no hay ninguna en este momento, no se sorprenda si comienzan a aparecer pronto).
Cada vez que se agrega un nuevo estudiante (usando el save()
método del servicio ), la propia matriz de estudiantes del servicio se actualizará y todos los demás objetos que compartan esa matriz también se actualizarán automáticamente.
Según el enfoque descrito anteriormente, su código podría verse así:
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.
});
};
}
]);
Una cosa con la que debe tener cuidado al usar este enfoque es nunca reasignar la matriz del servicio, porque entonces cualquier otro componente (por ejemplo, ámbitos) seguirá haciendo referencia a la matriz original y su aplicación se romperá.
Por ejemplo, para borrar la matriz en StudentService
:
/* DON'T DO THAT */
var clear = function () { students = []; }
/* DO THIS INSTEAD */
var clear = function () { students.splice(0, students.length); }
Vea también esta breve demostración .
PEQUEÑA ACTUALIZACIÓN:
Unas palabras para evitar la confusión que pueda surgir al hablar de utilizar un servicio, pero no de crearlo con la service()
función.
Citando los documentos en$provide
:
Un servicio angular es un objeto único creado por una fábrica de servicios . Estas fábricas de servicios son funciones que, a su vez, son creadas por un proveedor de servicios . Los proveedores de servicios son funciones de constructor. Cuando se crean instancias, deben contener una propiedad llamada $get
, que contiene la función de fábrica de servicios .
[...]
... el $provide
servicio tiene métodos auxiliares adicionales para registrar servicios sin especificar un proveedor:
- proveedor (proveedor) : registra un proveedor de servicios con $ injector
- constante (obj) : registra un valor / objeto al que pueden acceder los proveedores y servicios.
- valor (obj) : registra un valor / objeto al que solo pueden acceder los servicios, no los proveedores.
- factory (fn) : registra una función de fábrica de servicios, fn, que se incluirá en un objeto de proveedor de servicios, cuya propiedad $ get contendrá la función de fábrica dada.
- service (class) : registra una función de constructor, clase que se incluirá en un objeto de proveedor de servicios, cuya propiedad $ get instanciará un nuevo objeto utilizando la función de constructor dada.
Básicamente, lo que dice es que todos los servicios de Angular se registran usando $provide.provider()
, pero hay métodos de "atajos" para servicios más simples (dos de los cuales son service()
y factory()
).
Todo "se reduce" a un servicio, por lo que no importa mucho el método que utilice (siempre que los requisitos de su servicio puedan ser cubiertos por ese método).
Por cierto, provider
vs service
vs factory
es uno de los conceptos más confusos para los recién llegados de Angular, pero afortunadamente hay muchos recursos (aquí en SO) para facilitar las cosas. (Solo busca alrededor).
(Espero que eso lo aclare, avíseme si no es así).
service
ofactory
- terminará con un servicio Angular . Solo asegúrese de comprender cómo funciona cada uno y si se adapta a sus necesidades.$scope.students
Va a estar vacío, si la llamada ajax no finaliza? ¿O se$scope.students
llenará parcialmente, si este bloque de código está trabajando en progreso?students.push(student);
En lugar de intentar modificar el
$scope
dentro del servicio, puede implementar un$watch
dentro de su controlador para observar una propiedad en su servicio en busca de cambios y luego actualizar una propiedad en el$scope
. Aquí hay un ejemplo que puede probar en un controlador:Una cosa a tener en cuenta es que dentro de su servicio, para que la
students
propiedad sea visible, debe estar en el objeto Servicio o algothis
así:fuente
Bueno (uno largo) ... si insistes en tener
$scope
acceso dentro de un servicio, puedes:Crear un servicio getter / setter
Inyectarlo y almacenar el alcance del controlador en él
Ahora, obtenga el alcance dentro de otro servicio
fuente
Los servicios son singleton, y no es lógico que se inyecte un alcance en el servicio (que es el caso, de hecho, no se puede inyectar alcance en el servicio). Puede pasar el alcance como parámetro, pero esa también es una mala elección de diseño, porque tendría que editar el alcance en varios lugares, lo que dificultaría la depuración. El código para tratar las variables de alcance debe ir al controlador y las llamadas de servicio al servicio.
fuente
Puede hacer que su servicio desconozca por completo el alcance, pero en su controlador permita que el alcance se actualice de forma asincrónica.
El problema que tiene es porque no sabe que las llamadas http se realizan de forma asincrónica, lo que significa que no obtiene un valor de inmediato como podría. Por ejemplo,
Hay una forma sencilla de solucionar este problema y es proporcionar una función de devolución de llamada.
La forma:
Esto eliminó parte de la lógica de su negocio por brevedad y en realidad no he probado el código, pero algo como esto funcionaría. El concepto principal es pasar una devolución de llamada del controlador al servicio que se llama más adelante en el futuro. Si está familiarizado con NodeJS, este es el mismo concepto.
fuente
.then
métodos de promesa son un antipatrón .Me metí en la misma situación. Terminé con lo siguiente. Entonces, aquí no estoy inyectando el objeto scope en la fábrica, sino configurando el $ scope en el controlador mismo usando el concepto de promesa devuelto por $ http service.
fuente
El código para tratar las variables de alcance debe ir al controlador y las llamadas de servicio al servicio.
Puede inyectar
$rootScope
con el propósito de usar$rootScope.$broadcast
y$rootScope.$on
.De lo contrario, evite inyectarse
$rootScope
. Ver$rootScope
existe, pero puede usarse para el mal .fuente