Pasando el alcance actual a un servicio AngularJS

106

¿Es correcto pasar la "corriente" $scopea un servicio AngularJS?

Estoy en la situación en la que tengo un $ service sabiendo que solo lo consume un controlador, y me gustaría tener una referencia al alcance del controlador en los métodos $ service.

¿Es esto filosóficamente correcto?

¿O sería mejor transmitir eventos al $ rootScope y luego hacer que mi controlador los escuche?

CAROLINA DEL SUR
fuente
1
¿Quizás podría hacernos saber más específicamente lo que está intentando hacer? ¿Quizás no sea necesario introducir alcance en el servicio?
ganaraj
Bueno, no es tan difícil. Simplemente, me gustaría poder acceder a las $scopepropiedades y llamar $scope.$applycuando sea necesario.
SC
Además, digamos que quiero aplicar los cambios que provienen del $ service al $ scope. Espero que ahora esté más claro.
SC
8
Le sugiero que coloque las propiedades de $ scope a las que desea que acceda su servicio en el servicio en sí (en lugar de tenerlas en el controlador). Los servicios son mejores lugares para almacenar modelos / datos que los controladores.
Mark Rajcok
@MarkRajcock Estoy tratando de entender este problema también. Actualmente solo estoy llamando a un servicio y adjuntando los datos servidos a los del controlador $scope... ¿cómo accedería el controlador directamente a los datos en el servicio y los pasaría a la vista sin hacer esto?
drjimmie1976

Respuestas:

67

Para que el controlador sepa cuándo ocurre algo asíncrono, use las promesas angulares .

Para provocar el $apply, no necesita el alcance, puede llamar $rootScope.$apply, ya que no hay diferencia llamarlo en un alcance específico o en la raíz.

En cuanto a la lectura de la variable, sería mejor si recibiera los parámetros. Pero también podría leerlo desde un alcance como un parámetro de objeto, pero yo iría con el parámetro, eso haría que su interfaz de servicio sea mucho más clara.

Caio Cunha
fuente
Creo que esta es la respuesta que mejor resuelve mis dudas para principiantes de AngularJS.
SC
@Caio Cunha ¿Podría explicar por qué no es una buena idea pasar un alcance? Tengo exactamente este problema, quiero agregar algunas cosas a $scopetravés de una llamada a un servicio usando una executeSql()función asíncrona . Buscando en 3 opciones (1) use una devolución de llamada en la función asíncrona, luego llame $scope.$apply... esto funciona, pero es feo (2) pase $scopea la función asíncrona, luego llame theScope.$apply()... esto también funciona (3) use una promesa. ..no he probado esto todavía. ¿Por qué una promesa es la mejor manera? ¡Gracias!
drjimmie1976
15

Yo diría que si su funcionalidad es específica de un solo controlador, no necesita un servicio.

Las tareas de los controladores son manipular el modelo específico, mientras que un servicio debe ocuparse de tareas globales. Prefiero ceñirme a este paradigma en lugar de mezclar las cosas.

Esto es lo que dicen los doctores

Servicio

Los servicios angulares son singletons que realizan tareas específicas comunes a las aplicaciones web.

Controlador

En Angular, un controlador es una función de JavaScript (tipo / clase) que se usa para aumentar las instancias de angular Scope, excluyendo el alcance raíz.

PD: Aparte de eso, si necesita digerir, también puede inyectar $ rootScope dentro de su servicio.

F Lekschas
fuente
2
Gracias. Gran respuesta. Profundizando en mi situación, el punto es que estoy usando Service porque quiero un singleton. Solo hay un controlador que consume el servicio, pero este controlador se puede instanciar varias veces durante el ciclo de vida de la aplicación, por lo que realmente quiero que el servicio esté siempre en el mismo estado.
SC
De todos modos, la aclaración sobre la llamada $applyo $digestel $ rootScope tiene mucho sentido para mí.
SC
1
Todavía lo mantendría separado en un Servicio para probarlo.
bluehallu
Si la funcionalidad es específica de una instancia de un controlador, entonces puede omitir la implementación de un servicio, pero de lo contrario no, ya que sacrifica la capacidad de compartir el estado entre esas instancias. Además, el hecho de que hoy haya un solo tipo de controlador no significa que no habrá un controlador diferente que pueda aprovechar la funcionalidad mañana. Además, un servicio puede evitar sobrecargar el controlador. Entonces, no se trata tanto de si hay un solo controlador, sino de cuál es la funcionalidad que está en cuestión.
Nick
9

Si. Puede pasar $ scope al servicio cuando lo inicializa. En el constructor de servicios, puede asignar el alcance a algo como this._scope y luego hacer referencia al alcance dentro del servicio.

angular.module('blah').controller('BlahCtrl', function($scope, BlahService) {

    $scope.someVar = 4;

    $scope.blahService = new blahService($scope);

});

angular.module('blah').factory('blahService', function() {

    //constructor
    function blahService(scope) {
        this._scope = scope;

        this._someFunction()
    }

    //wherever you'd reference the scope
    blahService.prototype._someFunction = function() {

        this._scope['someVar'] = 5;

    }

    return blahService;

});
usuario12121234
fuente
1
+1, sin embargo, me gustaría ver una forma de conocer automáticamente cada controlador cada $scopevez que ese controlador inyecta el servicio, para no tener que llamar a un método en el servicio y pasarlo manualmente $scope.
Cody
2
@Cody No lo recomiendo ya que es contradictorio con la inyección de dependencia
Coldstar
¡De acuerdo, +1 por dispararme! Probablemente los carniceros también DIP - diría, a riesgo de ser redundantes.
Cody
Estás mezclando servicios con fábricas aquí. Esta solución utiliza una fábrica, que se diferencia de un servicio. La principal diferencia es que un servicio devuelve un objeto (singleton). Mientras que una fábrica devuelve una función, que se puede instanciar ( new MyFunction()). La pregunta se refería a un servicio, donde llamar newno es una opción.
Karvapallo
@Karvapallo Buen punto. Como contrapunto, creo que los servicios angulares se refieren a una fábrica, servicio y proveedor (ng-wat).
user12121234
6

Personalmente, creo que pasar $scopea un servicio es una mala idea , porque crea una referencia circular: el controlador depende del servicio y el servicio depende del alcance del controlador.

Además de ser confuso en términos de relaciones, cosas como esta terminan interponiéndose en el camino del recolector de basura.

Mi enfoque preferido es poner un objeto de dominio en el alcance del controlador y pasarlo al servicio. De esta manera, el servicio funciona independientemente de si se usa dentro de un controlador o tal vez dentro de otro servicio en el futuro.

Por ejemplo, si se supone que el servicio empuja y saca elementos de una matriz errors, mi código será:

var errors = [];
$scope.errors = errors;
$scope.myService = new MyService(errors);

El servicio interactúa luego con el controlador operando errors. Por supuesto, debo tener cuidado de no borrar nunca toda la referencia de la matriz, pero al final del día, eso es una preocupación general de JS.

Nunca querría usar la transmisión $applyy / o cosas similares, porque en mi humilde opinión, las buenas prácticas de OO siempre triunfarán sobre cualquier magia angular.

Marco Faustinelli
fuente
1
¿Este código tiene alguna diferencia con este ?:$scope.errors = []; $scope.myService = new MyService($scope.errors);
Soldeplata Saketos
@SoldeplataSaketos - Sí, lo hace. errorsvive independientemente de $scope. Ese es el objetivo de esta respuesta. Por favor, consulte el enlace que proporcioné en el texto. Salud.
Marco Faustinelli
1
Si entiendo correctamente su código, $scope.errorsestá apuntando a var errors, y los errores de variable me parecen redundantes, ya que es solo otro puntero. Una situación similar se me ocurre y que es abiertamente redundante es este pedazo de código: const errors = errors2 = errors3 = []; $scope.errors = errors;. ¿Está de acuerdo en que solo con el fragmento de código que proporcionó parece var errors = []redundante?
Soldeplata Saketos
No, no es. Me repito palabra por palabra: errorsvive independientemente de $scope. Debe comprender qué es un objeto de dominio, así como qué es una varasignación. Si el enlace que proporcioné no es suficiente, hay mucho más material disponible.
Marco Faustinelli
Bueno, eso dependerá únicamente de la implementación de la función MyService(errors). Según tengo entendido, el servicio debería generar una matriz de registro basada en el parámetro (que en este caso es un puntero). Para mí, ese es un mal patrón, ya que los servicios son singletons en angular. Si la implementación del servicio está bien programada, debería generar el arreglo en una variable interna (para seguir siendo un singleton). Por lo tanto, no tiene sentido inicializar la variable fuera del servicio.
Soldeplata Saketos