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を通過した各サービスに対してこれらのセットアップ呼び出しを1つ以上持つことです。

ソリューション3 -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);

これに汎用サービスを追加する必要がありますか?どのようにすればよいですか?


ここに私の答えをチェックしstackoverflow.com/a/51464584/4251431
バシールAL-MOMANIに

回答:


107

編集7/1/15:

私はかなり前にこの回答を書いて、しばらくの間、角度について多くのことを追い続けていませんでしたが、この回答はまだ比較的人気があるようですので、@ nicolasのポイントのいくつかを指摘したかったのです以下は良いです。1つは、$ rootScopeを挿入してヘルパーをアタッチすることで、コントローラーごとにヘルパーを追加する必要がなくなります。また、追加するものをAngularサービスまたはフィルターと見なす必要がある場合は、それらをコードにその方法で採用する必要があることに同意します。

また、現在のバージョン1.4.2以降、Angularは「Provider」APIを公開しています。これは、構成ブロックへの注入が許可されています。詳細については、次のリソースをご覧ください。

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

module.config内の値のAngularJS依存性注入

以下の実際のコードブロックを更新するつもりはありません。私は最近Angularをあまり積極的に使用しておらず、実際に新しいベストに準拠していることを快適に感じずに新しい答えを危険にさらしたくないからです。実践。他の誰かがそれに気づいているなら、ぜひそれのために行ってください。

編集2/3/14:

これについて考え、他のいくつかの回答を読んだ後、実際には、@ Brent Washburneと@Amogh Talpallikarが提案した方法のバリエーションを好むと思います。特にisNotString()などのユーティリティを探している場合。ここでの明らかな利点の1つは、角度コードの外でそれらを再利用でき、(サービスでは実行できない)構成関数内でそれらを使用できることです。

そうは言っても、適切にサービスであるべきものを再利用するための一般的な方法を探しているのであれば、古い答えはまだ良いと思います。

私が今やることは:

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>

これをすべて整理して、面倒な作業から解放する方法を次に示します。

コントローラー、サービス、ルーティングコード/構成を3つのファイル(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>

これにより、各コントローラーに1行のコードを追加するだけで、そのスコープにアクセスできる場所であればどこでもサービス機能にアクセスできます。


私はこれらの機能を20個持っているので、複数のコントローラーで使用したいと考えています。私はこれについて考えましたが、次のようなコードを作成することはそれほど現実的ではありません。$ scope.doSomething = ServiceName.functionName; 各コントローラの内部。質問をもう少し詳しく更新します。ありがとう
Alan2 2013年

サービスの関数ごとに行を追加する必要がある場合はそうですが、サービス全体(およびそのすべての関数)を1行でスコープに追加できる場合、それは理にかなっていると思います。あなたが言及した解決策2がどのように機能するかについて、私はあまり明確ではありませんか?
urban_raccoons 2013年

1
@urban_racoons:私もこの方法で始めましたが、残念ながら、そのようなサービスをconfigに挿入することはできません。インターセプター内のauth_serviceにアクセスして、ヘッダーにトークンを追加したいのですが、サービスを構成に挿入できないことに気付きました。定数のみが可能です。定数に関数を追加する方が良いアプローチだと思います。
Amogh Talpallikar 2013

1
私は主にJSの人ではないので、私はただ質問していますが、独自の名前空間を使用してコピーまたはシングルトンを作成しますか?大量のモジュールがある場合、特に1つのヘルパーのみを使用したい場合は、同じサービスのコピーを作成するのはメモリの浪費のようです。
Eric Keyte、2015年

3
@EricKeyte名前空間はオブジェクトリテラルです。これは、JSではかなり一般的なシングルトンの一種です。返信が遅れて申し訳ありません:)
urban_raccoons 2015

32

この古いスレッドに来て、私はそれを強調したかった

1°)module.runを介してrootscopeにユーティリティ関数を追加できます(?)。この目的のために、特定のルートレベルコントローラをインスタンス化する必要はありません。

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

2°)コードを別々のモジュールに編成する場合は、次のように、角度付きサービスまたはファクトリーを使用し、それらをrunブロックに渡される関数に注入する必要があります。

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 ServicesまたはFactoryに構造化した場合は、それらをフィルターコンストラクター内に挿入するだけです。

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

そしてあなたの見解では:

{{ aString | myFilter }}   

どちらのソリューションもrun-timeに関係しています。設定時間はどうですか?そこにユーティリティは必要ありませんか?
Cyril CHAPON、2015

プロバイダーが必要な構成時間(一種のサービス)角度のjsドキュメントをチェックアウト
nicolas

1
#3の解決策は私にとって最良のようです。フィルターを登録すると、他の場所で使用できます。通貨のフォーマットと日付のフォーマットに使用しました。
オラントビ2016

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 2014

私は実際にはあなたのアプローチが好きで、編集した回答に組み込んだ。グローバルな機能(および名前空間の汚染)のために誰かがあなたに反対票を投じた可能性があるように感じますが、多くの手持ちが必要だと思った場合、私が書いたものと同様のアプローチを組み込む可能性があると感じています。
urban_raccoons 2014年

個人的には、汎用的で小さなユーティリティ関数をグローバル化することに問題があるとは思えません。これは通常、コードベース全体で使用するものなので、だれでもすぐに慣れるでしょう。それらを言語の小さな拡張と見なします。
Cornel Masson、2014

あなたの編集の中であなたは「ドキュメントはコントローラー($ scope.doubleのような)の振る舞いを単に定義するように言っている」と述べています。ドキュメンテーションはユーティリティ関数をコントローラーに置くことを提案していると言っていますか?
losmescaleros 2017


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をときどき嫌う1つの問題を明確に示しています。「サービスを追加する」と言ってから、コードで新しいfactory()を作成します。設計パターンから見ると、それらは同じものではありません。ファクトリは通常、新しいオブジェクトを作成するために使用され、サービスは、いくつかの機能またはリソースを「提供」するために使用されます。そういう瞬間に「WT *、Angular」と言いたいです。
JustAMartin 2017

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>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.