147

I would like to add some utility functions to my AngularJS application. For example:

$scope.isNotString = function (str) {
    return (typeof str !== "string");
}

Is the best way to do this to add them as a service? From what I have read I can do this but then I would like to use these in my HTML pages so is it still possible if they are in a service? For example can I use the following:

 <button data-ng-click="doSomething()"
         data-ng-disabled="isNotString(abc)">Do Something
 </button>

Can someone give me an example of how I could add these. Should I create a service or is there some other way of doing it. Most important I would like these utility functions in a file and not combined with another part of the main set up.

I understand there's a few solutions but none of them are so clear.

Solution 1 - Proposed by Urban

$scope.doSomething = ServiceName.functionName;

The problem here is I have 20 functions and ten controllers. If I did this it would mean adding a lot of code to each controller.

Solution 2 - Proposed by me

    var factory = {

        Setup: function ($scope) {

            $scope.isNotString = function (str) {
                return (typeof str !== "string");
            }

The disadvantage of this is that at the start of every controller I would have one or more of these Setup calls to each service which passed the $scope.

Solution 3 - Proposed by Urban

The solution proposed by urban of creating a generic service looks good. Here's my main set up:

var app = angular
    .module('app', ['ngAnimate', 'ui.router', 'admin', 'home', 'questions', 'ngResource', 'LocalStorageModule'])
    .config(['$locationProvider', '$sceProvider', '$stateProvider',
        function ($locationProvider, $sceProvider, $stateProvider) {

            $sceProvider.enabled(false);
            $locationProvider.html5Mode(true);

Should I add the generic service to this and how could I do it ?

4

7 回答 7

109

编辑 7/1/15:

我很久以前写了这个答案,并且有一段时间没有跟上 angular,但似乎这个答案仍然比较流行,所以我想指出一点@nicolas下面做的很好。一方面,注入 $rootScope 并在那里附加帮助程序将使您不必为每个控制器添加它们。另外 - 我同意,如果您添加的内容应该被视为 Angular 服务或过滤器,那么它们应该以这种方式被采用到代码中。

此外,从当前版本 1.4.2 开始,Angular 公开了一个“Provider”API,允许将其注入到配置块中。有关更多信息,请参阅这些资源:

https://docs.angularjs.org/guide/module#module-loading-dependencies

AngularJS 依赖注入 module.config 中的值

我不认为我会更新下面的实际代码块,因为这些天我并没有真正积极地使用 Angular,而且我真的不想冒险一个新的答案而不觉得它实际上符合新的最佳实践。如果别人觉得可以,那就去吧。

编辑 2/3/14:

在考虑了这一点并阅读了其他一些答案之后,我实际上认为我更喜欢@Brent Washburne 和@Amogh Talpallikar 提出的方法的变体。特别是如果您正在寻找 isNotString() 或类似的实用程序。这里的明显优势之一是您可以在 Angular 代码之外重新使用它们,并且可以在配置函数内部使用它们(这是您无法使用服务执行的)。

话虽这么说,如果您正在寻找一种通用的方法来重用应该正确的服务,那么我认为旧答案仍然是一个很好的答案。

我现在要做的是:

应用程序.js:

var MyNamespace = MyNamespace || {};

 MyNamespace.helpers = {
   isNotString: function(str) {
     return (typeof str !== "string");
   }
 };

 angular.module('app', ['app.controllers', 'app.services']).                             
   config(['$routeProvider', function($routeProvider) {
     // Routing stuff here...
   }]);

控制器.js:

angular.module('app.controllers', []).                                                                                                                                                                                  
  controller('firstCtrl', ['$scope', function($scope) {
    $scope.helpers = MyNamespace.helpers;
  });

然后在您的部分中,您可以使用:

<button data-ng-click="console.log(helpers.isNotString('this is a string'))">Log String Test</button>

下面的旧答案:

最好将它们作为服务包括在内。如果您要在多个控制器中重复使用它们,将它们作为服务包括在内将使您不必重复代码。

如果您想在 html 部分中使用服务功能,那么您应该将它们添加到该控制器的范围:

$scope.doSomething = ServiceName.functionName;

然后在您的部分中,您可以使用:

<button data-ng-click="doSomething()">Do Something</button>

这里有一个方法可以让这一切井井有条并且免于太多麻烦:

将你的控制器、服务和路由代码/配置分成三个文件:controllers.js、services.js 和 app.js。最顶层的模块是“app”,它有 app.controllers 和 app.services 作为依赖。然后 app.controllers 和 app.services 可以在各自的文件中声明为模块。这个组织结构取自Angular Seed

应用程序.js:

 angular.module('app', ['app.controllers', 'app.services']).                             
   config(['$routeProvider', function($routeProvider) {
     // Routing stuff here...
   }]);  

服务.js:

 /* Generic Services */                                                                                                                                                                                                    
 angular.module('app.services', [])                                                                                                                                                                        
   .factory("genericServices", function() {                                                                                                                                                   
     return {                                                                                                                                                                                                              
       doSomething: function() {   
         //Do something here
       },
       doSomethingElse: function() {
         //Do something else here
       }
    });

控制器.js:

angular.module('app.controllers', []).                                                                                                                                                                                  
  controller('firstCtrl', ['$scope', 'genericServices', function($scope, genericServices) {
    $scope.genericServices = genericServices;
  });

然后在您的部分中,您可以使用:

<button data-ng-click="genericServices.doSomething()">Do Something</button>
<button data-ng-click="genericServices.doSomethingElse()">Do Something Else</button>

这样,您只需向每个控制器添加一行代码,就可以在可以访问该范围的任何地方访问任何服务功能。

于 2013-10-27T04:16:46.753 回答
33

谈到这个老话题,我想强调一点

1°) 实用功能可以(应该?)通过 module.run 添加到 rootscope。无需为此目的实例化特定的根级别控制器。

angular.module('myApp').run(function($rootScope){
  $rootScope.isNotString = function(str) {
   return (typeof str !== "string");
  }
});

2°)如果您将代码组织成单独的模块,您应该使用角度服务工厂,然后将它们注入传递给运行块的函数中,如下所示:

angular.module('myApp').factory('myHelperMethods', function(){
  return {
    isNotString: function(str) {
      return (typeof str !== 'string');
    }
  }
});

angular.module('myApp').run(function($rootScope, myHelperMethods){ 
  $rootScope.helpers = myHelperMethods;
});

3°)我的理解是,在视图中,在大多数情况下,您需要这些辅助函数来对您显示的字符串应用某种格式。在最后一种情况下,您需要使用角度过滤器

如果您将一些低级辅助方法构建到 Angular 服务或工厂中,只需将它们注入您的过滤器构造函数中:

angular.module('myApp').filter('myFilter', function(myHelperMethods){ 
  return function(aString){
    if (myHelperMethods.isNotString(aString)){
      return 
    }
    else{
      // something else 
    }
  }
);

在您看来:

{{ aString | myFilter }}   
于 2014-11-19T17:23:58.780 回答
6

我是否正确理解您只想定义一些实用程序方法并使其在模板中可用?

您不必将它们添加到每个控制器。只需为所有实用程序方法定义一个控制器并将该控制器附加到 <html> 或 <body> (使用 ngController 指令)。您在 <html> (表示任何地方,句点)或 <body> (除了 <head> 之外的任何地方)下的任何位置附加的任何其他控制器都将继承该 $scope 并可以访问这些方法。

于 2014-07-26T14:37:05.490 回答
4

添加实用函数的最简单方法是将它们保留在全局级别:

function myUtilityFunction(x) { return "do something with "+x; }

然后,添加实用程序函数(到控制器)的最简单方法是将其分配给$scope,如下所示:

$scope.doSomething = myUtilityFunction;

然后你可以这样称呼它:

{{ doSomething(x) }}

或像这样:

ng-click="doSomething(x)"

编辑:

最初的问题是添加实用功能的最佳方式是否是通过服务。我说不,如果函数足够简单(如isNotString()OP 提供的示例)。

编写服务的好处是用另一个(通过注入)替换它以进行测试。极端情况下,您是否需要将每一个实用功能都注入到您的控制器中?

文档说要简单地定义控制器中的行为(如$scope.double):http ://docs.angularjs.org/guide/controller

于 2014-01-08T04:33:28.603 回答
4

这是我使用的一种简单、紧凑且易于理解的方法。
首先,在你的 js 中添加一个服务。

app.factory('Helpers', [ function() {
      // Helper service body

        var o = {
        Helpers: []

        };

        // Dummy function with parameter being passed
        o.getFooBar = function(para) {

            var valueIneed = para + " " + "World!";

            return valueIneed;

          };

        // Other helper functions can be added here ...

        // And we return the helper object ...
        return o;

    }]);

然后,在您的控制器中,注入您的辅助对象并使用任何可用的函数,如下所示:

app.controller('MainCtrl', [

'$scope',
'Helpers',

function($scope, Helpers){

    $scope.sayIt = Helpers.getFooBar("Hello");
    console.log($scope.sayIt);

}]);
于 2016-11-01T02:33:13.047 回答
1

您也可以这样使用常量服务。在常量调用之外定义函数也允许它是递归的。

function doSomething( a, b ) {
    return a + b;
};

angular.module('moduleName',[])
    // Define
    .constant('$doSomething', doSomething)
    // Usage
    .controller( 'SomeController', function( $doSomething ) {
        $scope.added = $doSomething( 100, 200 );
    })
;
于 2016-06-23T20:03:22.437 回答
1

为什么不使用控制器继承,在 HeaderCtrl 范围内定义的所有方法/属性都可以在 ng-view 内的控制器中访问。$scope.servHelper 可以在您的所有控制器中访问。

    angular.module('fnetApp').controller('HeaderCtrl', function ($scope, MyHelperService) {
      $scope.servHelper = MyHelperService;
    });


<div ng-controller="HeaderCtrl">
  <div ng-view=""></div>
</div>
于 2016-05-05T08:14:13.137 回答