AngularJS: la forma correcta de enlazar a las propiedades de un servicio

162

Estoy buscando la mejor práctica de cómo enlazar a una propiedad de servicio en AngularJS.

He trabajado a través de múltiples ejemplos para comprender cómo enlazar propiedades en un servicio creado con AngularJS.

A continuación tengo dos ejemplos de cómo enlazar a propiedades en un servicio; ambos trabajan. El primer ejemplo usa enlaces básicos y el segundo ejemplo usa $ scope. $ Watch para enlazar a las propiedades del servicio

¿Se prefiere alguno de estos ejemplos cuando se vincula a propiedades en un servicio o hay otra opción que no conozco que se recomendaría?

La premisa de estos ejemplos es que el servicio debe actualizar sus propiedades "lastUpdated" y "llamadas" cada 5 segundos. Una vez que se actualizan las propiedades del servicio, la vista debe reflejar estos cambios. Ambos ejemplos funcionan con éxito; Me pregunto si hay una mejor manera de hacerlo.

Enlace Básico

El siguiente código se puede ver y ejecutar aquí: http://plnkr.co/edit/d3c16z

<html>
<body ng-app="ServiceNotification" >

    <div ng-controller="TimerCtrl1" style="border-style:dotted"> 
        TimerCtrl1 <br/>
        Last Updated: {{timerData.lastUpdated}}<br/>
        Last Updated: {{timerData.calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.timerData = Timer.data;
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

La otra forma en que resolví el enlace a las propiedades del servicio es usar $ scope. $ Watch en el controlador.

$ alcance. $ reloj

El siguiente código se puede ver y ejecutar aquí: http://plnkr.co/edit/dSBlC9

<html>
<body ng-app="ServiceNotification">
    <div style="border-style:dotted" ng-controller="TimerCtrl1">
        TimerCtrl1<br/>
        Last Updated: {{lastUpdated}}<br/>
        Last Updated: {{calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.$watch(function () { return Timer.data.lastUpdated; },
                function (value) {
                    console.log("In $watch - lastUpdated:" + value);
                    $scope.lastUpdated = value;
                }
            );

            $scope.$watch(function () { return Timer.data.calls; },
                function (value) {
                    console.log("In $watch - calls:" + value);
                    $scope.calls = value;
                }
            );
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

Soy consciente de que puedo usar $ rootscope. $ Broadcast en el servicio y $ root. $ On en el controlador, pero en otros ejemplos que he creado que usan $ broadcast / $ en la primera transmisión no son capturados por el controlador, pero las llamadas adicionales que se emiten se activan en el controlador. Si conoce una forma de resolver el problema de transmisión $ rootscope. $, Proporcione una respuesta.

Pero para repetir lo que mencioné anteriormente, me gustaría conocer las mejores prácticas de cómo vincular las propiedades de un servicio.


Actualizar

Esta pregunta fue originalmente formulada y respondida en abril de 2013. En mayo de 2014, Gil Birman proporcionó una nueva respuesta, que cambié como la respuesta correcta. Dado que la respuesta de Gil Birman tiene muy pocos votos positivos, mi preocupación es que las personas que lean esta pregunta ignoren su respuesta a favor de otras respuestas con muchos más votos. Antes de tomar una decisión sobre cuál es la mejor respuesta, le recomiendo la respuesta de Gil Birman.

Mike Barlow - BarDev
fuente
Creo que la respuesta de Josh David Miller es mejor que la de Gil Birman. Y usar $ watch, $ watchGroup y $ watchCollection puede incluso hacerlo aún mejor. La separación de preocupaciones es muy importante en las aplicaciones de tamaño mediano a grande.
Jonathan
@bardev Creo que ambas respuestas no fueron útiles, y los nuevos desarrolladores las entenderían completamente mal.
Zalaboza
El problema sobre el que está preguntando es sobre el comportamiento variable nativo de JavaScript de referenciar objetos, agregué una pequeña explicación a continuación
Zalaboza,

Respuestas:

100

Considere algunos pros y contras del segundo enfoque :

  • 0 en {{lastUpdated}} lugar de {{timerData.lastUpdated}}, lo que podría ser fácilmente {{timer.lastUpdated}}, lo que podría argumentar que es más legible (pero no discutamos ... Le doy a este punto una calificación neutral para que usted decida por usted mismo)

  • +1 Puede ser conveniente que el controlador actúe como una especie de API para el marcado, de modo que si de alguna manera la estructura del modelo de datos cambia, puede (en teoría) actualizar las asignaciones de API del controlador sin tocar el parcial html.

  • -1 Sin embargo, la teoría no siempre se practican y por lo general me encuentro tener que modificar el marcado y la lógica del controlador cuando los cambios son llamados para, de todas formas . Entonces, el esfuerzo adicional de escribir la API niega su ventaja.

  • -1 Además, este enfoque no es muy SECO.

  • -1 Si desea vincular los datos a ng-modelsu código, se vuelva aún menos SECO ya que debe volver a empaquetarlos $scope.scalar_valuesen el controlador para realizar una nueva llamada REST.

  • -0.1 Hay un pequeño éxito en el rendimiento que crea observadores adicionales. Además, si las propiedades de datos se adjuntan al modelo que no necesitan ser observadas en un controlador en particular, crearán una sobrecarga adicional para los observadores profundos.

  • -1 ¿Qué pasa si varios controladores necesitan los mismos modelos de datos? Eso significa que tiene varias API para actualizar con cada cambio de modelo.

$scope.timerData = Timer.data;está empezando a sonar muy tentador ahora mismo ... Profundicemos un poco más en ese último punto ... ¿De qué tipo de cambios de modelo estábamos hablando? ¿Un modelo en el back-end (servidor)? ¿O un modelo que se crea y vive solo en el front-end? En cualquier caso, lo que es esencialmente la API de mapeo de datos pertenece a la capa de servicio front-end (una fábrica o servicio angular). (Tenga en cuenta que su primer ejemplo, mi preferencia, no tiene una API de este tipo en la capa de servicio , lo cual está bien porque es lo suficientemente simple como para que no lo necesite).

En conclusión , no todo tiene que estar desacoplado. Y en cuanto a desacoplar el marcado por completo del modelo de datos, los inconvenientes superan las ventajas.


Los controladores, en general , no deben estar llenos de $scope = injectable.data.scalar's. Por el contrario, se deben rociar con $scope = injectable.data's, promise.then(..)' s y $scope.complexClickAction = function() {..}'s

Como un enfoque alternativo para lograr el desacoplamiento de datos y, por lo tanto, la encapsulación de la vista, el único lugar en el que realmente tiene sentido desacoplar la vista del modelo es con una directiva . Pero incluso allí, no $watchvalores escalares en las funciones controllero link. Eso no ahorrará tiempo ni hará que el código sea más fácil de mantener ni legible. Ni siquiera facilitará las pruebas, ya que las pruebas robustas en angular generalmente prueban el DOM resultante de todos modos . Por el contrario, en una demanda directiva de su API de datos en forma de objeto, y el favor usando sólo el $watchERS creado por ng-bind.


Ejemplo http://plnkr.co/edit/MVeU1GKRTN4bqA3h9Yio

<body ng-app="ServiceNotification">
    <div style="border-style:dotted" ng-controller="TimerCtrl1">
        TimerCtrl1<br/>
        Bad:<br/>
        Last Updated: {{lastUpdated}}<br/>
        Last Updated: {{calls}}<br/>
        Good:<br/>
        Last Updated: {{data.lastUpdated}}<br/>
        Last Updated: {{data.calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.data = Timer.data;
            $scope.lastUpdated = Timer.data.lastUpdated;
            $scope.calls = Timer.data.calls;
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 500);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>

ACTUALIZACIÓN : Finalmente volví a esta pregunta para agregar que no creo que ninguno de los enfoques sea "incorrecto". Originalmente había escrito que la respuesta de Josh David Miller era incorrecta, pero en retrospectiva sus puntos son completamente válidos, especialmente su punto sobre la separación de preocupaciones.

Dejando a un lado la separación de las preocupaciones (pero tangencialmente relacionadas), hay otra razón para la copia defensiva que no tuve en cuenta. Esta pregunta trata principalmente de leer datos directamente de un servicio. Pero, ¿qué sucede si un desarrollador de su equipo decide que el controlador necesita transformar los datos de una manera trivial antes de que la vista los muestre? (La cuestión de si los controladores deben transformar los datos es otra discusión). Si ella no hace una copia del objeto primero, puede causar involuntariamente regresiones en otro componente de la vista que consume los mismos datos.

Lo que esta pregunta realmente resalta son las deficiencias arquitectónicas de la aplicación angular típica (y realmente cualquier aplicación de JavaScript): acoplamiento estrecho de preocupaciones y mutabilidad de objetos. Recientemente me he enamorado de la aplicación de arquitectura con React y estructuras de datos inmutables. Hacerlo resuelve maravillosamente los siguientes dos problemas:

  1. Separación de preocupaciones : un componente consume todos sus datos a través de accesorios y tiene poca o ninguna dependencia de los singletons globales (como los servicios angulares), y no sabe nada sobre lo que sucedió por encima de él en la jerarquía de vistas.

  2. Mutabilidad : todos los accesorios son inmutables, lo que elimina el riesgo de mutación involuntaria de datos.

Angular 2.0 ahora está en camino de pedir prestado mucho de React para lograr los dos puntos anteriores.

Gil Birman
fuente
1
Cuanto más trabajo con AngularJS, más entiendo. Creo que los controladores AngularJS deberían ser lo más simples y delgados posibles. Al agregar $ relojes en el controlador, esto complica la lógica. Simplemente haciendo referencia al valor en el servicio es mucho más simple y parece ser más de la forma AngularJS. -
Mike Barlow - BarDev
3
Los "diez mandamientos" de AngularJS. Es declarativo por una razón.
pilau
La respuesta de Josh David Miller no es "INCORRECTA". Hay un tiempo y un lugar para todo.
JoshuaDavid
Creo que tienes razón, @FireCoding. Estoy planeando actualizar la respuesta.
Gil Birman
@GilBirman excelente respuesta. ¿Te importaría escribir y un ejemplo para la directiva? Para ilustrar lo que dijo "el único lugar en el que realmente tiene sentido desacoplar la vista del modelo es con una directiva. [...] Más bien, en una directiva exija su API de datos en forma de objeto y favorezca el uso de solo $ observadores creados por ng-bind ".
klode
78

Desde mi perspectiva, $watchsería la mejor práctica.

De hecho, puede simplificar un poco su ejemplo:

function TimerCtrl1($scope, Timer) {
  $scope.$watch( function () { return Timer.data; }, function (data) {
    $scope.lastUpdated = data.lastUpdated;
    $scope.calls = data.calls;
  }, true);
}

Eso es todo lo que necesitas.

Como las propiedades se actualizan simultáneamente, solo necesita un reloj. Además, dado que provienen de un único objeto bastante pequeño, lo cambié para observar la Timer.datapropiedad. El último parámetro pasado a $watchle dice que compruebe la igualdad profunda en lugar de asegurarse de que la referencia sea la misma.


Para proporcionar un poco de contexto, la razón por la que preferiría este método a colocar el valor del servicio directamente en el alcance es garantizar una separación adecuada de las preocupaciones. Su vista no debería necesitar saber nada sobre sus servicios para poder operar. El trabajo del controlador es unir todo; su trabajo es obtener los datos de sus servicios y procesarlos de la manera necesaria y luego brindarle a su vista los detalles que necesita. Pero no creo que su trabajo sea pasar el servicio directamente a la vista. De lo contrario, ¿qué hace el controlador incluso allí? Los desarrolladores de AngularJS siguieron el mismo razonamiento cuando optaron por no incluir ninguna "lógica" en las plantillas (por ejemplo, ifdeclaraciones).

Para ser justos, probablemente hay múltiples perspectivas aquí y espero otras respuestas.

Josh David Miller
fuente
3
¿Puedes elaborar un poco? ¿Prefieres los relojes $ porque la vista está menos acoplada al servicio? Es decir, {{lastUpdated}}vs.{{timerData.lastUpdated}}
Mark Rajcok
2
@BarDev, para usar Timer.dataen un $ watch, Timertiene que definirse en el $ scope, porque la expresión de cadena que pasa a $ watch se evalúa contra el alcance. Aquí hay un plunker que muestra cómo hacer que eso funcione. El parámetro objectEquality se documenta aquí , tercer parámetro, pero en realidad no se explica demasiado bien.
Mark Rajcok
2
En cuanto al rendimiento, $watches bastante ineficiente. Vea las respuestas en stackoverflow.com/a/17558885/932632 y stackoverflow.com/questions/12576798/…
Krym
11
@Kyrm Eso probablemente sea cierto en algunas circunstancias, pero cuando se trata del rendimiento, debemos buscar las mejoras de rendimiento "clínicamente significativas", en lugar de solo las estadísticamente significativas y generalizadas. Si hay un problema de rendimiento en una aplicación existente, entonces debe abordarse. De lo contrario, es solo un caso de optimización prematura, lo que conduce a un código más propenso a errores que no sigue las mejores prácticas y que no ha demostrado ningún beneficio.
Josh David Miller
1
Suponiendo que el observador usa una función para llamar al captador, entonces sí. Funciona bien. También soy un defensor de los servicios que devuelven instancias en una colección, donde las características getter y setter de es5 con bastante bien en su lugar.
Josh David Miller
19

Tarde a la fiesta, pero para futuros Googlers, no use la respuesta provista.

JavaScript tiene un mecanismo para pasar objetos por referencia, mientras que solo pasa una copia superficial para los valores "números, cadenas, etc.".

En el ejemplo anterior, en lugar de vincular los atributos de un servicio, ¿por qué no exponemos el servicio al alcance?

$scope.hello = HelloService;

Este enfoque simple hará que Angular pueda hacer enlaces bidireccionales y todas las cosas mágicas que necesita. No piratee su controlador con observadores o marcado innecesario.

Y si le preocupa que su vista sobrescriba accidentalmente sus atributos de servicio, utilícela definePropertypara que sea legible, enumerable, configurable o defina getters y setters. Puede obtener mucho control al hacer que su servicio sea más sólido.

Consejo final: si pasa más tiempo trabajando en su controlador que en sus servicios, lo está haciendo mal :(.

En ese código de demostración en particular que proporcionó, le recomendaría que haga:

 function TimerCtrl1($scope, Timer) {
   $scope.timer = Timer;
 }
///Inside view
{{ timer.time_updated }}
{{ timer.other_property }}
etc...

Editar:

Como mencioné anteriormente, puede controlar el comportamiento de sus atributos de servicio utilizando defineProperty

Ejemplo:

// Lets expose a property named "propertyWithSetter" on our service
// and hook a setter function that automatically saves new value to db !
Object.defineProperty(self, 'propertyWithSetter', {
  get: function() { return self.data.variable; },
  set: function(newValue) { 
         self.data.variable = newValue; 
         // let's update the database too to reflect changes in data-model !
         self.updateDatabaseWithNewData(data);
       },
  enumerable: true,
  configurable: true
});

Ahora en nuestro controlador si lo hacemos

$scope.hello = HelloService;
$scope.hello.propertyWithSetter = 'NEW VALUE';

¡nuestro servicio cambiará el valor propertyWithSettery también publicará el nuevo valor en la base de datos de alguna manera!

O podemos tomar cualquier enfoque que queramos.

Consulte la documentación de MDN para defineProperty.

Zalaboza
fuente
Estoy bastante seguro de que eso es lo que estaba describiendo anteriormente, con $scope.model = {timerData: Timer.data};solo adjuntarlo al modelo en lugar de directamente en Scope.
Scott Silvi
1
AFAIK, la referencia de objeto js funciona para todo en el Servicio. Codificando así: $ scope.controllerVar = ServiceVar, todo lo que haces en $ scope.controllerVar también se realiza en ServiceVar.
Kai Wang
@KaiWang está de acuerdo, a menos que haya decidido utilizar DefineAttribute, puede hacer que sus servicios tengan una función de establecimiento para evitar que los controladores muten accidentalmente sus datos de servicio.
Zalaboza,
12

Creo que esta pregunta tiene un componente contextual.

Si simplemente extrae datos de un servicio e irradia esa información a su vista, creo que enlazar directamente a la propiedad del servicio está bien. No quiero escribir mucho código repetitivo para simplemente asignar propiedades de servicio a propiedades de modelo para consumir en mi vista.

Además, el rendimiento en angular se basa en dos cosas. El primero es cuántos enlaces hay en una página. El segundo es lo caras que son las funciones getter. Misko habla de esto aquí

Si necesita realizar una lógica específica de instancia en los datos del servicio (a diferencia del masaje de datos aplicado dentro del propio servicio), y el resultado de esto afecta el modelo de datos expuesto a la vista, entonces diría que un $ watcher es apropiado, ya que siempre y cuando la función no sea terriblemente costosa. En el caso de una función costosa, sugeriría almacenar en caché los resultados en una variable local (al controlador), realizar sus operaciones complejas fuera de la función $ watcher, y luego vincular su alcance al resultado de eso.

Como advertencia, no debería colgar ninguna propiedad directamente de su $ scope. La $scopevariable NO es tu modelo. Tiene referencias a tu modelo.

En mi opinión, la "mejor práctica" para simplemente irradiar información del servicio hacia abajo para ver:

function TimerCtrl1($scope, Timer) {
  $scope.model = {timerData: Timer.data};
};

Y entonces tu vista contendría {{model.timerData.lastupdated}}.

Scott Silvi
fuente
cuidado con esa sugerencia, alguien que no sea experto en javascript podría intentar hacer eso con una propiedad que sea una cadena. En ese caso, JavaScript no hace referencia al objeto, sino que copia sin formato la cadena. (y eso es malo porque no se actualiza, si cambia)
Valerio
77
¿No cubrí eso con mi 'advertencia' de que siempre debe usar el punto (lo que significa que no lo cuelgue de $ scope, sino de $ scope.model). Si tiene $ scope.model.someStringProperty, y hace referencia a model.someStringProperty en su vista, se actualizará, porque los observadores internos estarán en el objeto, no en el accesorio.
Scott Silvi
6

Sobre la base de los ejemplos anteriores, pensé en unir de manera transparente una variable de controlador a una variable de servicio.

En el siguiente ejemplo, los cambios en la $scope.countvariable Controlador se reflejarán automáticamente en la countvariable Servicio .

En producción, en realidad estamos usando el enlace this para actualizar una identificación en un servicio que luego obtiene datos asincrónicamente y actualiza sus vars de servicio. Un enlace adicional significa que los controladores se actualizan automáticamente cuando el servicio se actualiza a sí mismo.

El código a continuación se puede ver trabajando en http://jsfiddle.net/xuUHS/163/

Ver:

<div ng-controller="ServiceCtrl">
    <p> This is my countService variable : {{count}}</p>
    <input type="number" ng-model="count">
    <p> This is my updated after click variable : {{countS}}</p>

    <button ng-click="clickC()" >Controller ++ </button>
    <button ng-click="chkC()" >Check Controller Count</button>
    </br>

    <button ng-click="clickS()" >Service ++ </button>
    <button ng-click="chkS()" >Check Service Count</button>
</div>

Servicio / Controlador:

var app = angular.module('myApp', []);

app.service('testService', function(){
    var count = 10;

    function incrementCount() {
      count++;
      return count;
    };

    function getCount() { return count; }

    return {
        get count() { return count },
        set count(val) {
            count = val;
        },
        getCount: getCount,
        incrementCount: incrementCount
    }

});

function ServiceCtrl($scope, testService)
{

    Object.defineProperty($scope, 'count', {
        get: function() { return testService.count; },
        set: function(val) { testService.count = val; },
    });

    $scope.clickC = function () {
       $scope.count++;
    };
    $scope.chkC = function () {
        alert($scope.count);
    };

    $scope.clickS = function () {
       ++testService.count;
    };
    $scope.chkS = function () {
        alert(testService.count);
    };

}
TomDotTom
fuente
Esta es una solución increíble, gracias, esto me ayudó mucho, una forma muy inteligente de hacerlo :)
Javis Perez
2

Creo que es una mejor manera de vincular el servicio en sí mismo en lugar de los atributos en él.

Este es el por qué:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>
<body ng-app="BindToService">

  <div ng-controller="BindToServiceCtrl as ctrl">
    ArrService.arrOne: <span ng-repeat="v in ArrService.arrOne">{{v}}</span>
    <br />
    ArrService.arrTwo: <span ng-repeat="v in ArrService.arrTwo">{{v}}</span>
    <br />
    <br />
    <!-- This is empty since $scope.arrOne never changes -->
    arrOne: <span ng-repeat="v in arrOne">{{v}}</span>
    <br />
    <!-- This is not empty since $scope.arrTwo === ArrService.arrTwo -->
    <!-- Both of them point the memory space modified by the `push` function below -->
    arrTwo: <span ng-repeat="v in arrTwo">{{v}}</span>
  </div>

  <script type="text/javascript">
    var app = angular.module("BindToService", []);

    app.controller("BindToServiceCtrl", function ($scope, ArrService) {
      $scope.ArrService = ArrService;
      $scope.arrOne = ArrService.arrOne;
      $scope.arrTwo = ArrService.arrTwo;
    });

    app.service("ArrService", function ($interval) {
      var that = this,
          i = 0;
      this.arrOne = [];
      that.arrTwo = [];

      $interval(function () {
        // This will change arrOne (the pointer).
        // However, $scope.arrOne is still same as the original arrOne.
        that.arrOne = that.arrOne.concat([i]);

        // This line changes the memory block pointed by arrTwo.
        // And arrTwo (the pointer) itself never changes.
        that.arrTwo.push(i);
        i += 1;
      }, 1000);

    });
  </script>
</body> 

Puedes jugarlo en este plunker .

Trantor Liu
fuente
1

Prefiero mantener a mis observadores lo menos posible. Mi razón se basa en mis experiencias y uno podría argumentarlo teóricamente.
El problema con el uso de los observadores es que puede usar cualquier propiedad en el alcance para llamar a cualquiera de los métodos en cualquier componente o servicio que desee.
En un proyecto del mundo real, muy pronto terminarás con una cadena de métodos no rastreables (mejor dicho, difíciles de rastrear) y cambios en los valores, lo que hace que el proceso de incorporación sea trágico.

Reyraa
fuente
0

Para vincular cualquier dato, lo que envía el servicio no es una buena idea (arquitectura), pero si lo necesita más, le sugiero 2 formas de hacerlo

1) puede obtener los datos que no están dentro de su servicio. Puede obtener datos dentro de su controlador / directiva y no tendrá ningún problema para vincularlos en cualquier lugar

2) puede usar eventos angularjs. Siempre que lo desee, puede enviar una señal (desde $ rootScope) y capturarla donde quiera. Incluso puede enviar datos sobre ese eventName.

Tal vez esto pueda ayudarle. Si necesita más con ejemplos, aquí está el enlace

http://www.w3docs.com/snippets/angularjs/bind-value-between-service-and-controller-directive.html

Hazarapet Tunanyan
fuente
-1

Qué pasa

scope = _.extend(scope, ParentScope);

¿Dónde ParentScope es un servicio inyectado?

tomascharad
fuente
-2

Las soluciones más elegantes ...

app.service('svc', function(){ this.attr = []; return this; });
app.controller('ctrl', function($scope, svc){
    $scope.attr = svc.attr || [];
    $scope.$watch('attr', function(neo, old){ /* if necessary */ });
});
app.run(function($rootScope, svc){
    $rootScope.svc = svc;
    $rootScope.$watch('svc', function(neo, old){ /* change the world */ });
});

Además, escribo EDA (arquitecturas controladas por eventos), por lo que tiendo a hacer algo como lo siguiente [versión simplificada]:

var Service = function Service($rootScope) {
    var $scope = $rootScope.$new(this);
    $scope.that = [];
    $scope.$watch('that', thatObserver, true);
    function thatObserver(what) {
        $scope.$broadcast('that:changed', what);
    }
};

Luego, pongo un oyente en mi controlador en el canal deseado y simplemente mantengo mi alcance local actualizado de esta manera.

En conclusión, no hay mucho de un "Best Practice" - más bien, su mayoría preferencia - siempre y cuando usted es mantener cosas sólidas y empleando acoplamiento débil. La razón por la que recomendaría este último código es porque los EDA tienen el acoplamiento más bajo posible por naturaleza. Y si no le preocupa demasiado este hecho, evitemos trabajar juntos en el mismo proyecto.

Espero que esto ayude...

Cody
fuente