如何在AngularJS应用程序中添加一些小的实用程序函数?


146

我想在AngularJS应用程序中添加一些实用程序功能。例如:

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

将它们添加为服务的最佳方法是吗?从我所阅读的内容中我可以做到,但是然后我想在HTML页面中使用它们,因此如果它们在服务中仍然可行吗?例如,我可以使用以下内容:

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

有人可以举例说明如何添加这些内容。我应该创建服务还是有其他实现方法。最重要的是,我希望将这些实用程序功能存储在文件中,而不要与主要设置的另一部分结合使用。

我了解有一些解决方案,但没有一个如此明确。

解决方案1 -Urban提出

$scope.doSomething = ServiceName.functionName;

这里的问题是我有20个功能和10个控制器。如果执行此操作,则意味着要向每个控制器添加很多代码。

解决方案2-我提议

    var factory = {

        Setup: function ($scope) {

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

这样做的缺点是,在每个控制器启动时,我会对通过$ scope的每个服务有一个或多个这些Setup调用。

解决方案3 -Urban提出

Urban提出的创建通用服务的解决方案看起来不错。这是我的主要设置:

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);

我是否应该在其中添加通用服务,我该怎么做?


Answers:


107

编辑7/1/15:

我在很久以前就写了这个答案,并且有一段时间没跟上角度了,但似乎这个答案仍然相对受欢迎,所以我想指出一点@nicolas下面做的很好。首先,注入$ rootScope并附加那里的助手将使您不必为每个控制器添加它们。另外-我同意,如果您要添加的内容应被视为Angular服务或过滤器,则应以这种方式将其应用于代码中。

另外,从当前版本1.4.2开始,Angular公开了一个“提供程序” API,该API可以注入到配置块中。有关更多信息,请参见以下资源:

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

AngularJS依赖注入module.config内部的值

我认为我不会在下面更新实际的代码块,因为这些天我并没有真正积极地使用Angular,并且我真的不想冒一个新的答案而又不觉得它实际上符合最新的最佳做法。实践。如果有人愿意接受,那么一定要去做。

编辑2/3/14:

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

话虽如此,如果您正在寻找一种通用的方式来重用应该正确地用作服务的内容,那么我认为旧的答案仍然是一个不错的选择。

我现在要做的是:

app.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...
   }]);

controller.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

app.js:

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

services.js:

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

controller.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>

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


我可能有二十个这些功能,我想在多个控制器中使用它们。我考虑过这一点,但拥有这样的代码并不实际:$ scope.doSomething = ServiceName.functionName; 每个控制器内部。我将用更多详细信息更新我的问题。谢谢
Alan2

是的,如果您需要为服务中的每个功能添加一行,那是有道理的,但是如果您可以将整个服务(包括所有功能)一行添加到范围中,我认为这是有道理的。对于您提到的解决方案2可能如何工作,我不太清楚。
urban_raccoons 2013年

1
@urban_racoons:我也是以这种方式开始的,但是不幸的是您不能在config中注入这样的服务。我想在拦截器中访问我的auth_service以将令牌添加到标头中,但是后来我意识到该服务无法注入配置中。只有常量可以。我认为将函数添加到常量应该是更好的方法。
Amogh Talpallikar

1
我只是问,因为我主要不是JS家伙,但是使用您自己的名称空间会产生副本还是单例?如果您有大量的模块,那么拥有相同服务的副本似乎会浪费内存,尤其是在您只想使用一个助手的情况下。
埃里克·凯特

3
@EricKeyte命名空间是一个对象文字,这是一种单例,在JS中很常见。抱歉,回复延迟:)
urban_raccoons 2015年

32

在这个旧线程上,我想强调一点

可以(应该?)通过module.run将1°)实用程序功能添加到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.module('myApp').filter('myFilter', function(myHelperMethods){ 
  return function(aString){
    if (myHelperMethods.isNotString(aString)){
      return 
    }
    else{
      // something else 
    }
  }
);

在您看来:

{{ aString | myFilter }}   

两种解决方案都涉及run-时间。那配置时间呢?我们是否不需要公用事业?
Cyril CHAPON 2015年

配置时间,您需要提供程序(一种服务)签出有角度的js文档
nicolas 2015年

1
#3解决方案对我来说似乎是最好的。注册过滤器后,您可以在其他任何地方使用它。我将其用于货币格式和日期格式。
Olantobi '16

6

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

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


1
这绝对是最好的方法。只要拥有一个实用程序控制器并将其放入整个项目的包装器/容器div中,其中的所有控制器都将继承:<div class="main-container" ng-controller="UtilController as util">然后在任何内部视图中:<button ng-click="util.isNotString(abc)">
Ian J Miller

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


将实用程序功能作为服务可以使您有选择地在控制器中访问它们。如果没有控制器使用它们,则不会实例化该服务。
StuR

实际上,我有点喜欢您的方法,并将其纳入我的编辑答案中。我觉得有人可能会因为全局功能(以及名称空间污染)而对您不满意,但是我觉得如果您认为有必要进行大量操作,那么您可能会采用一种与我写出的方法类似的方法。 。
urban_raccoons 2014年

就个人而言,我看不到将通用的小型实用程序功能全局化的问题。通常情况下,您将在整个代码库中使用这些东西,因此任何人都会很快熟悉它们。将它们视为语言的小扩展。
Cornel Masson 2014年

在您的编辑中,您提到“文档说只是定义控制器中的行为(例如$ scope.double)”。您是说文档建议将实用程序功能放入控制器中吗?
losmescaleros

@losmescaleros是的,请阅读文档docs.angularjs.org/guide/controller
Brent Washburne

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);

}]);

2
这清楚地显示了一个为什么我有时不喜欢Angular的问题:说“添加服务” ...然后在代码中创建一个新的factory()。从设计模式来看,它们不是一回事-工厂通常用于生产新对象,而服务则用于“服务”某些功能或资源。在这样的时刻,我想说“ WT *,Angular”。
JustAMartin

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 );
    })
;

0

为什么不使用控制器继承,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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.