Posso disponibilizar uma função em todos os controladores angulares?

191

Se eu tiver uma função utilitária fooque queira chamar de qualquer lugar dentro da minha ng-appdeclaração. Existe alguma maneira de torná-lo acessível globalmente na configuração do meu módulo ou preciso adicioná-lo ao escopo em todos os controladores?

Ludwig Magnusson
fonte
Não tenho 100% de certeza disso, mas há uma chance de que você também possa defini-lo em seu módulo como este: module.value('myFunc', function(a){return a;});e injetar pelo nome em seus controladores. (Se alguém quiser evitar fazer um serviço de)
user2173353
Significando que eu tenho que adicioná-lo a todos os controladores manualmente. $ rootScope é o caminho a percorrer para o que eu queria fazer há quase 2 anos =)
Ludwig Magnusson
ESTÁ BEM. :) Eu apenas uso diretivas com escopo isolado com mais frequência do que controladores simples e tenho que injetar tudo de qualquer maneira. Eu gosto do estilo de código modular que isso fornece. Além disso, você não precisa mexer nos escopos pai de forma alguma e não precisa pesquisar muito de onde vêm as variáveis ​​do escopo. :)
user2173353

Respostas:

290

Você basicamente tem duas opções, defini-lo como um serviço ou colocá-lo no seu escopo raiz. Sugiro que você faça um serviço para evitar poluir o escopo raiz. Você cria um serviço e o disponibiliza em seu controlador da seguinte maneira:

<!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>

Se isso não é uma opção para você, você pode adicioná-lo ao escopo raiz assim:

<!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>

Dessa forma, todos os seus modelos podem chamar globalFoo()sem ter que passar para o modelo a partir do controlador.

Anders Ekdahl
fonte
5
Na primeira solução, e se houver toneladas de foo()funções? Fazer um $scope.callFoo()invólucro para cada um deles é muito trabalhoso. Como posso "anexar" todas as funções de uma biblioteca ao escopo para que ela possa ser usada no modelo? Eu tenho uma grande biblioteca de conversão de unidades que quero que esteja disponível no meu modelo.
AlexStack #
3
Minha pergunta também. Eu tentei isso e funciona: você pode "anexá-lo" ao invés $scope.callFoo = myService.foo;de criar um novo invólucro em cada local que você deseja usá-lo.
Fitter Man
1
Obrigado pela sua ajuda, eu queria saber como disponibilizar uma função de alteração de idioma em todo o meu aplicativo e $ rootScope fez o trabalho. Eu queria manter o módulo de tradução separado do aplicativo para poder conectá-lo a outros aplicativos também.
Paulo Pedroso
Eu sugiro usar um valor angular em vez de uma fábrica para este caso específico, a menos que você planeje ter várias funções em um serviço. Se for apenas uma função, torne-o um Valor.
Nicholas Blasgen
I obteve o erro: [$ injector: UNPR]
Mark Thien
53

Você também pode combiná-los, eu acho:

<!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>
ric
fonte
7
Acho que essa deve ser a resposta correta, justificada pela resposta de @Praym. Não faz sentido especificar uma dependência de serviço em 10 controladores diferentes.
jvannistelrooy
O serviço pode incluir métodos / funções que podem propriedades / variáveis ​​CRUD no $rootScope?
jezmck
44

Embora a primeira abordagem seja defendida como a abordagem "do tipo angular", sinto que isso acrescenta custos indiretos.

Considere se eu quero usar esta função myservice.foo em 10 controladores diferentes. Terei que especificar essa dependência 'myService' e, em seguida, a propriedade $ scope.callFoo scope em todas as dez. Isso é simplesmente uma repetição e de alguma forma viola o princípio DRY.

Considerando que, se eu usar a abordagem $ rootScope, especificarei essa função global gobalFoo apenas uma vez e estará disponível em todos os meus controladores futuros, não importando quantos.

Praym
fonte
5
Pode haver algum valor na documentação dos controladores, onde eles recebem essa chamada de serviço global. Se você colocasse um de seus controladores em outro aplicativo, seria menos claro de onde veio essa função global. Eu ouço reconhecer seu argumento embora.
Matthew Payne
Ele só precisa ser colocado no escopo se você precisar chamá-lo da visualização. No controlador, você pode chamá-lo diretamente do serviço.
Mcv 06/11
10
Este é um comentário, não uma resposta
Elliott
A variável $ rootScope sempre nula na atualização da página; nesse caso, você não obterá a função. É por isso que é bom injetar o serviço e usar sua referência no aplicativo.
Ehsan Hafeez
4

O AngularJs possui " Serviços " e " Fábricas " apenas para problemas como o seu. Estes costumam ter algo global entre Controladores, Diretivas, Outros Serviços ou qualquer outro componente do angularjs. Você pode definir funções, armazenar dados, fazer funções de cálculo ou o que quer que seja quiser dentro dos Serviços e usá-los no 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
          });
       }
       ....
    }
  }])

se você precisar de mais

Saiba mais sobre por que precisamos dos serviços e fábricas da AngularJs

Hazarapet Tunanyan
fonte
0

Sou um pouco mais novo no Angular, mas o que achei útil fazer (e bastante simples) é que criei um script global que carrego na minha página antes do script local com variáveis ​​globais que eu preciso acessar em todas as páginas de qualquer maneira. Nesse script, criei um objeto chamado "globalFunctions" e adicionei as funções que preciso acessar globalmente como propriedades. por exemplo globalFunctions.foo = myFunc();. Então, em cada script local, escrevi $scope.globalFunctions = globalFunctions;e instantaneamente tenho acesso a qualquer função que adicionei ao objeto globalFunctions no script global.

Isso é um pouco de solução alternativa e não tenho certeza de que o ajude, mas definitivamente me ajudou, pois eu tinha muitas funções e foi difícil adicionar todas elas a cada página.

Izzy
fonte
1
Só estou curioso por que os votos negativos? Sou novo e gostaria de saber dos profissionais.
Izzy
Parece uma solução funcional para mim. A única coisa que eu advogaria, para garantir que seu escopo seja suficientemente isolado, é criar uma classe de utilitário Javascript raiz global e interromper seus métodos utilitários para que você não pise acidentalmente em algum outro nome de função no diretório vasto mar de coisas injetadas pela Angular.
JE Carter II
Uma coisa a ser observada, e talvez por que isso seja diminuído, você só pode usar essas funções em seus modelos, e não nos módulos .ts, pois suas chamadas de função não resolveriam em tempo de compilação. Esta é a razão para fazê-lo da maneira "angular". Mas, se você deseja adicionar decoradores e outros itens aos seus modelos, um arquivo de utilitário global é simples e adequado.
JE Carter II