AngularJS 컨트롤러를 확장하는 데 권장되는 방법은 무엇입니까?


193

꽤 비슷한 세 개의 컨트롤러가 있습니다. 이 세 가지 기능을 확장하고 공유하는 컨트롤러를 갖고 싶습니다.

답변:


302

아마도 당신은 컨트롤러를 확장하지 않습니다하지만 컨트롤러를 확장하거나 하나의 컨트롤러를 여러 컨트롤러의 믹스 인을 만드는 것이 가능하다.

module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) {
    // Initialize the super class and extend it.
    angular.extend(this, $controller('CtrlImpl', {$scope: $scope}));
     Additional extensions to create a mixin.
}]);

상위 컨트롤러가 생성되면 그 안에 포함 된 로직도 실행됩니다. $scope값만 전달 하면되지만 자세한 내용은 $ controller ()를 참조하십시오 . 다른 모든 값은 정상적으로 주입됩니다.

@mwarren , Angular 의존성 주입으로 문제를 자동으로 처리합니다. 필요한 경우 $ scope를 주입하기 만하면되지만 원하는 경우 다른 주입 된 값을 무시할 수 있습니다. 다음 예제를 보자.

(function(angular) {

	var module = angular.module('stackoverflow.example',[]);

	module.controller('simpleController', function($scope, $document) {
		this.getOrigin = function() {
			return $document[0].location.origin;
		};
	});

	module.controller('complexController', function($scope, $controller) {
		angular.extend(this, $controller('simpleController', {$scope: $scope}));
	});

})(angular);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.js"></script>

<div ng-app="stackoverflow.example">
    <div ng-controller="complexController as C">
        <span><b>Origin from Controller:</b> {{C.getOrigin()}}</span>
    </div>
</div>

'complexController'에 의해 생성 될 때 $ document가 'simpleController'에 전달되지 않지만 $ document가 우리에게 주입됩니다.


1
지금까지 가장 빠르고, 가장 깨끗하고 쉬운 솔루션입니다! 감사!
MK

완벽하고 멋진 솔루션!
Kamil Lach

8
난 당신이 필요하지 않습니다 생각 $.extend(), 당신은 단순히 전화 할 수 있습니다$controller('CtrlImpl', {$scope: $scope});
tomraithel

5
@tomraithel angular.extend(또는 $.extend)을 사용하지 않는 것은 실제로 확장하는 것을 의미 $scope하지만, 기본 컨트롤러가 일부 속성 (예 this.myVar=5this.myVarangular.extend
:)을 정의

1
대단해! : 그냥 확인에 필요한 모든 기능을 확장하기 위해 기억 할 수 있도록, 즉 내가 좋아하는 뭔가했다 handleSubmitClick부를 것이다 handleLogin차례에 있던 loginSuccessloginFail. 그래서, 내 확장 된 컨트롤러에서 나는 다음에 과부하를했다 handleSubmitClick, handleLogin그리고 loginSucess올바른에 대한 loginSuccess기능을 사용할 수 있습니다.
저스틴 크루스

52

상속을 위해 표준 JavaScript 상속 패턴을 사용할 수 있습니다. 다음은 사용하는 데모입니다$injector

function Parent($scope) {
  $scope.name = 'Human';
  $scope.clickParent = function() {
    $scope.name = 'Clicked from base controller';
  }    
}

function Child($scope, $injector) {
  $injector.invoke(Parent, this, {$scope: $scope});
  $scope.name = 'Human Child';
  $scope.clickChild = function(){
    $scope.clickParent();
  }       
}

Child.prototype = Object.create(Parent.prototype);

controllerAs구문 을 사용하는 경우 (내가 권장하는) 고전 상속 패턴을 사용하는 것이 훨씬 쉽습니다.

function BaseCtrl() {
  this.name = 'foobar';
}
BaseCtrl.prototype.parentMethod = function () {
  //body
};

function ChildCtrl() {
  BaseCtrl.call(this);
  this.name = 'baz';
}
ChildCtrl.prototype = Object.create(BaseCtrl.prototype);
ChildCtrl.prototype.childMethod = function () {
  this.parentMethod();
  //body
};

app.controller('BaseCtrl', BaseCtrl);
app.controller('ChildCtrl', ChildCtrl);

또 다른 방법은 기본 컨트롤러가되는 "추상적 인"생성자 함수를 만드는 것입니다.

function BaseController() {
  this.click = function () {
    //some actions here
  };
}

module.controller('ChildCtrl', ['$scope', function ($scope) {
  BaseController.call($scope);
  $scope.anotherClick = function () {
    //other actions
  };
}]);

이 주제에 대한 블로그 게시물


16

글쎄, 나는 당신이 무엇을 달성하고자하는지 확실하지 않지만 일반적으로 서비스가 갈 길입니다. Angular의 스코프 상속 특성을 사용하여 컨트롤러간에 코드를 공유 할 수도 있습니다.

<body ng-controller="ParentCtrl">
 <div ng-controller="FirstChildCtrl"></div>
 <div ng-controller="SecondChildCtrl"></div>
</body>

function ParentCtrl($scope) {
 $scope.fx = function() {
   alert("Hello World");
 });
}

function FirstChildCtrl($scope) {
  // $scope.fx() is available here
}

function SecondChildCtrl($scope) {
  // $scope.fx() is available here
}

비슷한 작업을 수행하는 컨트롤러에서 동일한 변수와 기능을 공유하십시오 (하나는 편집, 다른 것은 생성 등). 이것은 확실히 해결책 중 하나입니다.
vladexologija

1
$ scope 상속은 허용되는 답변보다 훨씬 낫습니다.
snez

결국 이것은 가장 각진 방법으로 보였다. childController가 선택할 수있는 $ scope 값을 설정하는 세 개의 다른 페이지에 세 개의 매우 간단한 parentController가 있습니다. 원래 확장에 대해 생각한 childController에는 모든 컨트롤러 논리가 포함되어 있습니다.
mwarren

하지 않을 $scope.$parent.fx( ) 가 실제로 정의 된 곳이기 때문에, 그것을 할 수있는 많은 청소기 방법이?
Akash

15

컨트롤러를 확장하지 않습니다. 동일한 기본 기능을 수행하는 경우 해당 기능을 서비스로 이동해야합니다. 이 서비스는 컨트롤러에 주입 할 수 있습니다.


3
고마워하지만 이미 서비스 (저장, 삭제 등)를 사용하는 4 가지 기능이 있으며 세 컨트롤러 모두 동일합니다. 확장이 옵션이 아닌 경우 'mixin'가능성이 있습니까?
vladexologija

4
@vladexologija 나는 Bart에 동의합니다. 서비스가 믹스 인이라고 생각합니다. 가능한 많은 로직을 컨트롤러에서 서비스로 이동 시키십시오. 따라서 비슷한 작업을 수행해야하는 3 개의 컨트롤러가 있다면 서비스가 올바른 접근 방식 인 것 같습니다. 확장 컨트롤러는 Angular에서 자연스럽지 않습니다.
ganaraj

6
@vladexologija 여기에 무슨 의미인지 예가 있습니다 : jsfiddle.net/ERGU3 매우 기본이지만 아이디어를 얻을 수 있습니다.
Bart

3
대답은 아무런 주장이나 추가 설명 없이는 쓸모가 없습니다. 또한 OP의 요점을 다소 놓치고 있다고 생각합니다. 이미 공유 서비스가 있습니다. 당신이하는 유일한 일은 그 서비스를 직접 노출시키는 것입니다. 좋은 생각인지 모르겠습니다. 범위에 액세스해야하는 경우에도 접근이 실패합니다. 그러나 귀하의 추론에 따라 범위를 속성의 범위로보기에 명시 적으로 노출하여 인수로 전달할 수 있습니다.
더 나은 올리버

6
전형적인 예는 사이트에 두 개의 양식 중심 보고서가있는 경우입니다. 각 보고서는 많은 동일한 데이터에 의존하고 각 보고서는 많은 공유 서비스를 사용합니다. 이론적으로 수십 개의 AJAX 호출을 사용하여 별도의 모든 서비스를 하나의 큰 서비스에 넣은 다음 'getEverythingINeedForReport1'및 'getEverythingINeedForReport2'와 같은 공용 메소드를 사용하여 하나의 거대한 범위 객체로 설정할 수 있습니다. 본질적으로 컨트롤러 로직을 서비스에 넣습니다. 일부 상황에서는 컨트롤러 확장에 유스 케이스가 있습니다.
tobylaroni

10

기사 에서 얻은 또 다른 좋은 해결책 :

// base controller containing common functions for add/edit controllers
module.controller('Diary.BaseAddEditController', function ($scope, SomeService) {
    $scope.diaryEntry = {};

    $scope.saveDiaryEntry = function () {
        SomeService.SaveDiaryEntry($scope.diaryEntry);
    };

    // add any other shared functionality here.
}])

module.controller('Diary.AddDiaryController', function ($scope, $controller) {
    // instantiate base controller
    $controller('Diary.BaseAddEditController', { $scope: $scope });
}])

module.controller('Diary.EditDiaryController', function ($scope, $routeParams, DiaryService, $controller) {
    // instantiate base controller
    $controller('Diary.BaseAddEditController', { $scope: $scope });

    DiaryService.GetDiaryEntry($routeParams.id).success(function (data) {
        $scope.diaryEntry = data;
    });
}]);

1
이것은 나를 위해 매우 잘 작동했습니다. 하나의 컨트롤러로 시작하여 매우 유사한 컨트롤러를 만든 다음 코드를 DRYer로 만들려는 상황에서 쉽게 리팩토링 할 수 있다는 장점이 있습니다. 코드를 변경할 필요가 없습니다. 코드를 뽑아서 완료하십시오.
Eli Albert

7

서비스를 주입하여 서비스를 생성하고 모든 컨트롤러에서 해당 동작을 상속 할 수 있습니다.

app.service("reusableCode", function() {

    var reusableCode = {};

    reusableCode.commonMethod = function() {
        alert('Hello, World!');
    };

    return reusableCode;
});

그런 다음 컨트롤러에서 위의 재사용 가능한 코드 서비스에서 확장하려고합니다.

app.controller('MainCtrl', function($scope, reusableCode) {

    angular.extend($scope, reusableCode);

    // now you can access all the properties of reusableCode in this $scope
    $scope.commonMethod()

});

데모 플 런커 : http://plnkr.co/edit/EQtj6I0X08xprE8D0n5b?p=preview


5

다음과 같이 시도해 볼 수 있습니다 (테스트하지 않았습니다).

function baseController(callback){
    return function($scope){
        $scope.baseMethod = function(){
            console.log('base method');
        }
        callback.apply(this, arguments);
    }
}

app.controller('childController', baseController(function(){

}));

1
예. 확장 할 필요는 없으며 컨텍스트 만 사용하면됩니다
TaylorMac

4

서비스 , 공장 또는 공급자로 확장 할 수 있습니다 . 그것들은 동일하지만 다른 정도의 유연성을 가지고 있습니다.

여기 팩토리를 사용하는 예 : http://jsfiddle.net/aaaflyvw/6KVtj/2/

angular.module('myApp',[])

.factory('myFactory', function() {
    var myFactory = {
        save: function () {
            // saving ...
        },
        store: function () {
            // storing ...
        }
    };
    return myFactory;
})

.controller('myController', function($scope, myFactory) {
    $scope.myFactory = myFactory;
    myFactory.save(); // here you can use the save function
});

그리고 여기서 저장 기능을 사용할 수도 있습니다.

<div ng-controller="myController">
    <input ng-blur="myFactory.store()" />
</div>

4

$ controller ( 'ParentController', {$ scope : $ scope})를 직접 사용할 수 있습니다

module.controller('Parent', ['$scope', function ($scope) {
    //code
}])

module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) {
    //extend parent controller
    $controller('CtrlImpl', {$scope: $scope});
}]);


1

나는 이것을하는 함수를 썼다 :

function extendController(baseController, extension) {
    return [
        '$scope', '$injector',
        function($scope, $injector) {
            $injector.invoke(baseController, this, { $scope: $scope });
            $injector.invoke(extension, this, { $scope: $scope });
        }
    ]
}

다음과 같이 사용할 수 있습니다.

function() {
    var BaseController = [
        '$scope', '$http', // etc.
        function($scope, $http, // etc.
            $scope.myFunction = function() {
                //
            }

            // etc.
        }
    ];

    app.controller('myController',
        extendController(BaseController,
            ['$scope', '$filter', // etc.
            function($scope, $filter /* etc. */)
                $scope.myOtherFunction = function() {
                    //
                }

                // etc.
            }]
        )
    );
}();

장점 :

  1. 기본 컨트롤러를 등록 할 필요가 없습니다.
  2. 어떤 컨트롤러도 $ controller 또는 $ injector 서비스에 대해 알 필요가 없습니다.
  3. 각도의 배열 주입 구문과 잘 작동합니다. 자바 스크립트가 축소 될 때 필수적입니다.
  4. 모든 하위 컨트롤러에 서비스를 추가하거나 전달할 필요없이 추가 컨트롤러 를 추가로 기본 컨트롤러 에 추가 할 수 있습니다.

단점 :

  1. 기본 컨트롤러는 전역 범위를 오염시킬 위험이있는 변수로 정의해야합니다. 익명의 자체 실행 함수로 모든 것을 래핑하여 사용 예제에서 이것을 피했지만 모든 자식 컨트롤러를 동일한 파일로 선언해야합니다.
  2. 이 패턴은 html에서 직접 인스턴스화되는 컨트롤러에는 효과적이지만 $ controller () 서비스를 통해 코드에서 생성하는 컨트롤러에는 좋지 않습니다. 인젝터에 의존하기 때문에 인젝터에 의존하지 않고 추가 인젝션을 직접 주입 할 수 없기 때문입니다. 호출 코드의 -service 매개 변수

1

컨트롤러를 나쁜 연습으로 확장하는 것을 고려합니다. 오히려 공유 논리를 서비스에 넣습니다. 자바 스크립트의 확장 객체는 다소 복잡 해지는 경향이 있습니다. 상속을 사용하려면 typescript를 권장합니다. 여전히 얇은 컨트롤러는 제 관점에서 더 나은 방법입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.