¿Puedo hacer que una función esté disponible en cada controlador en angular?


191

Si tengo una función de utilidad fooque quiero poder llamar desde cualquier lugar dentro de mi ng-appdeclaración. ¿Hay alguna manera de hacer que sea accesible globalmente en la configuración de mi módulo o necesito agregarlo al alcance en cada controlador?


No estoy 100% seguro de esto, pero existe la posibilidad de que también pueda definirlo en su módulo de esta manera: module.value('myFunc', function(a){return a;});y luego inyectarlo por nombre en sus controladores. (Si uno quiere evitar hacer un servicio)
user2173353

Lo que significa que tengo que agregarlo a cada controlador manualmente. $ rootScope es el camino a seguir para lo que quería hacer hace casi 2 años =)
Ludwig Magnusson

OKAY. :) Solo uso directivas con un alcance aislado con mayor frecuencia que los controladores simples y tengo que inyectar todo de todos modos. Me gusta el estilo de código modular que proporciona. Además, no tiene que meterse con los ámbitos primarios de ninguna manera y no tiene que buscar mucho de dónde provienen sus variables de alcance. :)
user2173353

Respuestas:


290

Básicamente tiene dos opciones, definirlo como un servicio o colocarlo en su ámbito raíz. Te sugiero que hagas un servicio fuera de él para evitar contaminar el alcance raíz. Crea un servicio y lo pone a disposición en su controlador de la siguiente manera:

<!doctype html>
<html ng-app="myApp">
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
    <script type="text/javascript">
    var myApp = angular.module('myApp', []);

    myApp.factory('myService', function() {
        return {
            foo: function() {
                alert("I'm foo!");
            }
        };
    });

    myApp.controller('MainCtrl', ['$scope', 'myService', function($scope, myService) {
        $scope.callFoo = function() {
            myService.foo();
        }
    }]);
    </script>
</head>
<body ng-controller="MainCtrl">
    <button ng-click="callFoo()">Call foo</button>
</body>
</html>

Si esa no es una opción para usted, puede agregarla al alcance raíz de esta manera:

<!doctype html>
<html ng-app="myApp">
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
    <script type="text/javascript">
    var myApp = angular.module('myApp', []);

    myApp.run(function($rootScope) {
        $rootScope.globalFoo = function() {
            alert("I'm global foo!");
        };
    });

    myApp.controller('MainCtrl', ['$scope', function($scope){

    }]);
    </script>
</head>
<body ng-controller="MainCtrl">
    <button ng-click="globalFoo()">Call global foo</button>
</body>
</html>

De esa manera, todas sus plantillas pueden llamar globalFoo() sin tener que pasarlas a la plantilla desde el controlador.


55
En la primera solución, ¿qué pasa si hay toneladas de foo()funciones? Hacer un $scope.callFoo()envoltorio para cada uno de ellos es demasiado trabajo. ¿Cómo puedo "adjuntar" todas las funciones de una biblioteca al alcance para que pueda usarse en la plantilla? Tengo una gran biblioteca de conversión de unidades que quiero que esté disponible en mi plantilla.
AlexStack

3
Mi pregunta también. Probé esto y funciona: puedes "adjuntarlo" diciendo $scope.callFoo = myService.foo;en lugar de crear un nuevo contenedor en cada lugar que quieras usar.
Fitter Man

1
Gracias por su ayuda, me preguntaba cómo poner a disposición una función de cambio de idioma en mi aplicación y $ rootScope hizo el trabajo. Quería mantener el módulo de traducción separado de la aplicación para poder conectarlo también a otras aplicaciones.
Paulo Pedroso

Sugeriría usar un Valor angular en lugar de una Fábrica para este caso específico a menos que planee tener múltiples funciones dentro de un servicio. Si es solo una función, conviértala en un valor.
Nicholas Blasgen

Tengo un error: [$ injector: unpr]
Mark Thien

53

También puedes combinarlos, supongo:

<!doctype html>
<html ng-app="myApp">
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
    <script type="text/javascript">
        var myApp = angular.module('myApp', []);

        myApp.factory('myService', function() {
            return {
                foo: function() {
                    alert("I'm foo!");
                }
            };
        });

        myApp.run(function($rootScope, myService) {
            $rootScope.appData = myService;
        });

        myApp.controller('MainCtrl', ['$scope', function($scope){

        }]);

    </script>
</head>
<body ng-controller="MainCtrl">
    <button ng-click="appData.foo()">Call foo</button>
</body>
</html>

77
Creo que esta debería ser la respuesta correcta, justificada por la respuesta de @Praym. No tiene sentido especificar una dependencia de servicio en 10 controladores diferentes.
jvannistelrooy

¿Puede el servicio incluir métodos / funciones que pueden CRUD propiedades / variables en el $rootScope?
jezmck

44

Aunque el primer enfoque se defiende como el enfoque 'como el angular', creo que esto agrega gastos generales.

Considere si quiero usar esta función myservice.foo en 10 controladores diferentes. Tendré que especificar esta dependencia 'myService' y luego la propiedad de alcance $ scope.callFoo en las diez. Esto es simplemente una repetición y de alguna manera viola el principio DRY.

Mientras que, si uso el enfoque $ rootScope, especifico esta función global gobalFoo solo una vez y estará disponible en todos mis futuros controladores, sin importar cuántos.


55
Puede haber algún valor en la "documentación" de los controladores donde obtienen esa llamada de servicio global. Si fueras a tirar uno de tus controladores a otra aplicación, sería menos claro de dónde proviene esa función global. Aunque oigo reconocer tu argumento.
Matthew Payne

Solo tiene que ponerse en el alcance si necesita llamarlo desde la vista. En el controlador, puede llamarlo directamente desde el servicio.
mcv

10
Este es un comentario, no una respuesta
Elliott

La variable $ rootScope siempre es nula en la actualización de la página en ese caso, no obtendrá la función. Es por eso que es bueno inyectar el servicio y usar su referencia en la aplicación.
Ehsan Hafeez

4

AngularJs tiene " Servicios " y " Fábricas " solo para problemas como el suyo. Se utilizan para tener algo global entre Controladores, Directivas, Otros Servicios o cualquier otro componente angularjs. Puede definir funciones, almacenar datos, realizar funciones de cálculo o lo que sea querer dentro de Servicios y usarlos en AngularJs Components como Global .like

angular.module('MyModule', [...])
  .service('MyService', ['$http', function($http){
    return {
       users: [...],
       getUserFriends: function(userId){
          return $http({
            method: 'GET',
            url: '/api/user/friends/' + userId
          });
       }
       ....
    }
  }])

si necesitas mas

Encuentre más sobre por qué necesitamos los servicios y las fábricas de AngularJs


0

Soy un poco más nuevo en Angular, pero lo que me pareció útil (y bastante simple) es que hice un script global que cargo en mi página antes del script local con variables globales a las que necesito acceder en todas las páginas de todos modos. En ese script, creé un objeto llamado "globalFunctions" y agregué las funciones a las que necesito acceder globalmente como propiedades. por ej globalFunctions.foo = myFunc();. Luego, en cada script local, escribí$scope.globalFunctions = globalFunctions; e instantáneamente tengo acceso a cualquier función que agregué al objeto globalFunctions en el script global.

Esta es una solución alternativa y no estoy seguro de que te ayude, pero definitivamente me ayudó, ya que tenía muchas funciones y fue una molestia agregarlas todas en cada página.


1
Tengo curiosidad por qué los votos negativos? Soy nuevo y me gustaría saber de los profesionales.
Izzy

Me parece una solución de trabajo. Lo único que recomendaría, para asegurarme de que su alcance esté lo suficientemente aislado, es crear una clase de utilidad de Javascript raíz global y colgar sus métodos de utilidad para evitar que accidentalmente pise algún otro nombre de función en el vasto mar de cosas inyectadas por Angular.
JE Carter II

Una cosa a tener en cuenta, y tal vez por qué esto se rechaza, solo podría usar esas funciones en sus plantillas, no en sus módulos .ts, ya que sus llamadas de función no se resolverían en el momento de la compilación. Esta es la razón para hacerlo de forma "angular". Pero, si solo desea agregar decoradores y demás a sus plantillas, un archivo de utilidad global es simple y está bien.
JE Carter II
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.