Lo $scopeque 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 $rootScopey 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 $scopeconvertir 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 StudentServiceestá a cargo de manejar los datos de los estudiantes, puede StudentServicemantener 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 $provideservicio 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, providervs servicevs factoryes 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í).
serviceofactory- terminará con un servicio Angular . Solo asegúrese de comprender cómo funciona cada uno y si se adapta a sus necesidades.$scope.studentsVa a estar vacío, si la llamada ajax no finaliza? ¿O se$scope.studentsllenará parcialmente, si este bloque de código está trabajando en progreso?students.push(student);En lugar de intentar modificar el
$scopedentro del servicio, puede implementar un$watchdentro 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
studentspropiedad sea visible, debe estar en el objeto Servicio o algothisasí:fuente
Bueno (uno largo) ... si insistes en tener
$scopeacceso 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
.thenmé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
$rootScopecon el propósito de usar$rootScope.$broadcasty$rootScope.$on.De lo contrario, evite inyectarse
$rootScope. Ver$rootScopeexiste, pero puede usarse para el mal .fuente