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를 통과 한 각 서비스에 대해 하나 이상의 이러한 설정 호출이 있다는 것입니다.

솔루션 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
Basheer AL-MOMANI

답변:


107

7/1/15 편집 :

나는 꽤 오래 전에이 답변을 썼고 한동안 각도를 많이 유지하지 않았지만이 답변이 여전히 상대적으로 인기가있는 것처럼 보이므로 @nicolas 아래는 좋습니다. 우선, $ 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 () 또는 이와 유사한 유틸리티를 찾고 있다면. 여기서 명백한 장점 중 하나는 각도 코드 외부에서 다시 사용할 수 있으며 구성 기능 내에서 사용할 수 있다는 것입니다 (서비스로는 할 수 없음).

즉, 서비스를 올바르게 사용해야하는 일반적인 방법을 찾고 있다면 이전의 대답은 여전히 ​​좋은 방법이라고 생각합니다.

내가 지금 할 일은 :

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>

이렇게하면 각 컨트롤러에 한 줄의 코드 만 추가하고 해당 범위에 액세스 할 수있는 모든 서비스 기능에 액세스 할 수 있습니다.


이 기능 중 20 개가 있고 여러 컨트롤러에서 사용하고 싶습니다. 나는 이것에 대해 생각했지만 다음과 같은 코드를 갖는 것은 실용적이지 않습니다. $ scope.doSomething = ServiceName.functionName; 각 컨트롤러 내부. 좀 더 자세한 내용으로 질문을 업데이트하겠습니다. 감사합니다
Alan2

예, 서비스의 각 기능에 대한 라인을 추가 해야하는 경우에는 의미가 있지만 전체 서비스 (모든 기능과 함께)를 한 라인의 범위에 추가 할 수 있다면 의미가 있다고 생각합니다. 언급 한 솔루션 2가 어떻게 작동하는지 잘 모르겠습니다.
urban_raccoons

1
@urban_racoons : 나는 또한 이런 식으로 시작했지만 불행히도 당신은 config에 그러한 서비스를 주입 할 수 없습니다. 인터셉터 내 auth_service에 액세스하여 헤더에 토큰을 추가하고 싶었지만 서비스를 구성에 삽입 할 수 없다는 것을 깨달았습니다. 상수 만 가능합니다. 상수에 함수를 추가하는 것이 더 나은 방법이라고 생각합니다.
Amogh Talpallikar

1
나는 주로 JS 사람이 아니기 때문에 질문하고 있지만 자신의 네임 스페이스를 사용하면 사본이나 싱글 톤을 생성합니까? 많은 모듈이있는 경우, 특히 하나의 도우미 만 사용하려는 경우 동일한 서비스 사본을 갖는 것은 메모리 낭비로 보입니다.
Eric Keyte

3
@EricKeyte 네임 스페이스는 객체 리터럴로, JS에서 일반적으로 사용되는 일종의 싱글 톤입니다. 답변이 늦어

32

이 오래된 실에 와서 강조하고 싶었습니다.

1 °) 유틸리티 기능은 module.run을 통해 루트 스코프에 추가해야합니다. 이 목적으로 특정 루트 레벨 컨트롤러를 설치할 필요는 없습니다.

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

설정 시간 당신은 공급자 (서비스의 일종)가 필요합니다 각도 js 문서
nicolas

1
# 3 솔루션은 나에게 최고인 것 같습니다. 필터가 등록되면 다른 곳에서 사용할 수 있습니다. 통화 형식 및 날짜 형식으로 사용했습니다.
Olantobi

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)">
이안 J 밀러

4

유틸리티 기능을 추가하는 가장 쉬운 방법은 전역 기능을 유지하는 것입니다.

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

그런 다음 컨트롤러에 유틸리티 함수를 추가하는 가장 간단한 방법은 다음과 같이에 함수를 할당하는 것입니다 $scope.

$scope.doSomething = myUtilityFunction;

그러면 다음과 같이 호출 할 수 있습니다.

{{ doSomething(x) }}

또는 이와 같이 :

ng-click="doSomething(x)"

편집하다:

원래의 질문은 유틸리티 기능을 추가하는 가장 좋은 방법은 서비스를 통하는 것입니다. 기능이 충분히 단순하다면 (아니오가 isNotString()제공 한 예 와 같이) 아니오라고 말합니다 .

서비스 작성의 이점은 테스트 목적으로 서비스를 다른 것으로 (주입을 통해) 대체하는 것입니다. 극단적으로, 모든 단일 유틸리티 기능을 컨트롤러에 주입해야합니까?

문서는 단순히 (같은 컨트롤러의 동작을 정의 말한다 $scope.double) : http://docs.angularjs.org/guide/controller


유틸리티 기능을 서비스로 사용하면 컨트롤러에서 선택적으로 액세스 할 수 있습니다. 컨트롤러가 사용하지 않으면 서비스가 인스턴스화되지 않습니다.
StuR

나는 실제로 당신의 접근 방식을 좋아하고 그것을 편집 된 답변에 통합했습니다. 전역 기능 (및 네임 스페이스 오염)으로 인해 누군가가 당신을 억압 한 느낌이 들지만, 많은 손을 잡는 것이 필요하다고 생각하면 내가 쓴 것과 비슷한 접근법을 통합했을 가능성이 있습니다 .
urban_raccoons

개인적으로, 일반적이고 작은 유틸리티 기능을 전역으로 만드는 데 문제가 없습니다. 일반적으로 코드베이스 전체에서 사용하는 것이기 때문에 누구나 매우 빠르게 익숙해 질 것입니다. 그것들을 언어의 작은 확장으로보십시오.
Cornel Masson

편집에서 "문서에는 컨트롤러의 동작을 정의하기 만합니다 (예 : $ scope.double)". 설명서에 컨트롤러에 유틸리티 기능을 넣을 것을 제안한다고 말하는가?
losmescaleros

예 @losmescaleros, 설명서에서 "스코프 개체에 동작을 추가"섹션을 읽어 docs.angularjs.org/guide/controller
브렌트 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

컨트롤러 상속을 사용하지 않는 이유는 ng-view 내의 컨트롤러에서 HeaderCtrl 범위에 정의 된 모든 메서드 / 속성에 액세스 할 수 있다는 것입니다. $ 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.