다른 컨트롤러에서 지시어 컨트롤러의 메서드 호출


118

자체 컨트롤러가있는 지시문이 있습니다. 아래 코드를 참조하십시오.

var popdown = angular.module('xModules',[]);

popdown.directive('popdown', function () {
    var PopdownController = function ($scope) {
        this.scope = $scope;
    }

    PopdownController.prototype = {
        show:function (message, type) {
            this.scope.message = message;
            this.scope.type = type;
        },

        hide:function () {
            this.scope.message = '';
            this.scope.type = '';
        }
    }

    var linkFn = function (scope, lElement, attrs, controller) {

    };

    return {
        controller: PopdownController,
        link: linkFn,
        replace: true,
        templateUrl: './partials/modules/popdown.html'
    }

});

이는 오류 / 알림 / 경고에 대한 알림 시스템입니다. 내가 원하는 것은 다른 컨트롤러 (지시문이 아닌) show에서이 컨트롤러 에서 함수를 호출하는 것 입니다. 그렇게 할 때 링크 기능이 일부 속성이 변경되었음을 감지하고 일부 애니메이션을 수행하기를 원합니다.

다음은 내가 요구하는 것을 예시하는 코드입니다.

var app = angular.module('app', ['RestService']);

app.controller('IndexController', function($scope, RestService) {
    var result = RestService.query();

    if(result.error) {
        popdown.notify(error.message, 'error');
    }
});

호출 할 때 그래서 showpopdown지시어 컨트롤러, 링크 기능은 트리거 및 애니메이션을 수행해야합니다. 어떻게 할 수 있습니까?


popdown페이지 에서 지시문에 대한 호출을 어디에 배치하고 있습니까? 다른 컨트롤러가 모두 액세스 할 수 있어야하는 한 위치에 있습니까, 아니면 다른 위치에 여러 개의 팝업이 있습니까?
satchmorun 2013

내 index.html에는 다음이 있습니다. <div ng-view> </ div> <div popdown> </ div> 기본적으로 전역 적으로 사용할 수있는 팝업 인스턴스는 1 개뿐입니다.
user253530 2013

1
popdown.show(...)대신 글을 쓰려고했던 것 같아요 popdown.notify(...). 그렇지 않으면 알림 기능이 다소 혼란 스럽습니다.
lanoxx 2014

어디에서 왔 popdown.notify습니까? .notifiy방법은, 내 말은
녹색

답변:


167

이것은 흥미로운 질문입니다. 저는 이와 같은 것을 어떻게 구현할 것인지 생각하기 시작했습니다.

나는 이것을 생각해 냈다 .

기본적으로 컨트롤러에서 지시문을 호출하는 대신 모든 팝 다운 논리를 수용하는 모듈을 만들었습니다.

var PopdownModule = angular.module('Popdown', []);

모듈에 두 가지를 넣었습니다. 하나 factory는 어디에나 주입 할 수있는 API와 directive실제 팝 다운 요소의 동작을 정의하는 것입니다.

공장은 기능의 몇 가지를 정의 success하고 error변수의 몇 가지를 추적 :

PopdownModule.factory('PopdownAPI', function() {
    return {
        status: null,
        message: null,
        success: function(msg) {
            this.status = 'success';
            this.message = msg;
        },
        error: function(msg) {
            this.status = 'error';
            this.message = msg;
        },
        clear: function() {
            this.status = null;
            this.message = null;
        }
    }
});

지시문은 컨트롤러에 API를 주입하고 API에서 변경 사항을 감시합니다 (편의상 부트 스트랩 CSS를 사용하고 있습니다).

PopdownModule.directive('popdown', function() {
    return {
        restrict: 'E',
        scope: {},
        replace: true,
        controller: function($scope, PopdownAPI) {
            $scope.show = false;
            $scope.api = PopdownAPI;

            $scope.$watch('api.status', toggledisplay)
            $scope.$watch('api.message', toggledisplay)

            $scope.hide = function() {
                $scope.show = false;
                $scope.api.clear();
            };

            function toggledisplay() {
                $scope.show = !!($scope.api.status && $scope.api.message);               
            }
        },
        template: '<div class="alert alert-{{api.status}}" ng-show="show">' +
                  '  <button type="button" class="close" ng-click="hide()">&times;</button>' +
                  '  {{api.message}}' +
                  '</div>'
    }
})

그런 다음 다음에 app의존 하는 모듈을 정의합니다 Popdown.

var app = angular.module('app', ['Popdown']);

app.controller('main', function($scope, PopdownAPI) {
    $scope.success = function(msg) { PopdownAPI.success(msg); }
    $scope.error   = function(msg) { PopdownAPI.error(msg); }
});

HTML은 다음과 같습니다.

<html ng-app="app">
    <body ng-controller="main">
        <popdown></popdown>
        <a class="btn" ng-click="success('I am a success!')">Succeed</a>
        <a class="btn" ng-click="error('Alas, I am a failure!')">Fail</a>
    </body>
</html>

완전히 이상적인 것인지는 모르겠지만, 글로벌 팝 다운 지시문으로 통신을 설정하는 합리적인 방법 인 것 같았습니다.

다시 참고로 바이올린 .


10
+1 지시문 외부에서 지시문의 함수를 호출해서는 안됩니다. 이는 나쁜 습관입니다. 서비스를 사용하여 지시문이 읽는 전역 상태를 관리하는 것은 매우 일반적이며 올바른 접근 방식입니다. 더 많은 응용 프로그램에는 알림 대기열 및 모달 대화 상자가 포함됩니다.
Josh David Miller

7
정말 특별한 대답! jQuery와 Backbone에서 온 우리들에게 유용한 예제
Brandon

11
이런 식으로이 모듈을 사용하여 동일한 뷰에서 여러 지시문을 인스턴스화 할 수 있습니까? 이 지시문의 특정 인스턴스의 성공 또는 오류 함수를 어떻게 호출 할 수 있습니까?
ira

3
@ira 상태 및 메시지 객체의 맵 (또는 목록)을 유지하도록 팩토리를 변경 한 다음 지시문의 이름 속성을 사용하여 목록에서 필요한 항목을 식별 할 수 있습니다. 따라서 success(msg)html을 호출하는 대신sucess(name, msg) 올바른 이름을 가진 지시문을 선택하도록 합니다.
lanoxx 2014

5
@JoshDavidMiller 디렉티브에서 메소드를 호출하는 것이 왜 나쁜 습관이라고 생각합니까? 지시문이 의도 한대로 일부 DOM 로직을 캡슐화하는 경우 API를 사용하는 컨트롤러가 필요에 따라 메소드를 호출 할 수 있도록 API를 노출하는 것이 당연한 일입니까?
Paul Taylor

27

이벤트를 사용하여 Popdown을 트리거 할 수도 있습니다.

다음 은 satchmorun의 솔루션을 기반으로 한 바이올린 입니다. PopdownAPI를 생략하고 최상위 컨트롤러는 대신 $broadcast범위 체인 아래에서 '성공'및 '오류'이벤트를 처리합니다.

$scope.success = function(msg) { $scope.$broadcast('success', msg); };
$scope.error   = function(msg) { $scope.$broadcast('error', msg); };

그런 다음 Popdown 모듈은 이러한 이벤트에 대한 핸들러 함수를 등록합니다. 예 :

$scope.$on('success', function(event, msg) {
    $scope.status = 'success';
    $scope.message = msg;
    $scope.toggleDisplay();
});

이것은 적어도 작동하며 나에게 멋지게 분리 된 솔루션 인 것 같습니다. 어떤 이유로 든 이것이 좋지 않다고 생각되면 다른 사람들에게 차임 할 것입니다.


1
제가 생각할 수있는 한 가지 단점은 선택한 답변에서 PopdownAPI 만 필요하다는 것입니다 (DI에서 쉽게 사용할 수 있음). 이 경우 메시지를 브로드 캐스트하려면 컨트롤러의 범위에 액세스해야합니다. 어쨌든 매우 간결 해 보입니다.
Julian

복잡성을 낮추고 여전히 느슨하게 결합되어 있기 때문에 단순한 사용 사례에 대한 서비스 접근 방식보다 더 좋습니다
Patrick Favre 2014 년

11

당신은 또한 같은 부모 범위에 지시어의 컨트롤러를 노출 수 ngFormname특성을 수행합니다 http://docs.angularjs.org/api/ng.directive:ngForm

여기에서 어떻게 달성 할 수 있는지 매우 기본적인 예를 찾을 수 있습니다. http://plnkr.co/edit/Ps8OXrfpnePFvvdFgYJf?p=preview

이 예제에서는 메서드 가 myDirective있는 전용 컨트롤러 $clear(지시문에 대한 매우 간단한 공용 API)가 있습니다. 이 컨트롤러를 부모 범위에 게시하고 지시문 외부에서이 메서드를 호출 할 수 있습니다.


이것은 컨트롤러 간의 관계가 필요합니다. OP는 메시지 센터를 원했기 때문에 이상적이지 않을 수 있습니다. 그러나 당신의 접근 방식을 배우는 것도 매우 좋았습니다. 많은 상황에서 유용하며 말했듯이 angular 자체가 그것을 사용합니다.
fasfsfgs jul.

나는 satchmorun이 제공 한 예를 따르려고 노력하고 있습니다. 런타임에 일부 html을 생성하고 있지만 지시문의 템플릿을 사용하지 않습니다. 추가 된 HTML에서 호출 할 함수를 지정하기 위해 지시문의 컨트롤러를 사용하고 있지만 함수가 호출되지 않습니다. 기본적으로 다음과 같은 지시문이 있습니다. {..};}, 내 html : "<a href="" ng-click="function1('itemtype')">
Mark

이것은 지시문이 싱글 톤이 아닌 경우 지시문 API를 노출 할 수있는 유일한 우아한 솔루션입니다! $scope.$parent[alias]내부 각도 API를 사용하는 것과 같은 냄새가 나기 때문에 여전히 사용하는 것을 좋아하지 않습니다 . 그러나 여전히 단일하지 않은 지시문에 대한 더 우아한 솔루션을 찾을 수 없습니다. 브로드 캐스팅 이벤트와 같은 다른 변형 또는 지시어 API 냄새를 위해 상위 컨트롤러에서 빈 개체를 정의합니다.
Ruslan Stelmachenko 2015

3

나는 훨씬 더 나은 해결책을 얻었다.

여기에 내 지시문이 있습니다. 지시문의 개체 참조에 삽입하고 지시문 코드에 invoke 함수를 추가하여 확장했습니다.

app.directive('myDirective', function () {
    return {
        restrict: 'E',
        scope: {
        /*The object that passed from the cntroller*/
        objectToInject: '=',
        },
        templateUrl: 'templates/myTemplate.html',

        link: function ($scope, element, attrs) {
            /*This method will be called whet the 'objectToInject' value is changes*/
            $scope.$watch('objectToInject', function (value) {
                /*Checking if the given value is not undefined*/
                if(value){
                $scope.Obj = value;
                    /*Injecting the Method*/
                    $scope.Obj.invoke = function(){
                        //Do something
                    }
                }    
            });
        }
    };
});

매개 변수를 사용하여 HTML에서 지시문 선언 :

<my-directive object-to-inject="injectedObject"></ my-directive>

내 컨트롤러 :

app.controller("myController", ['$scope', function ($scope) {
   // object must be empty initialize,so it can be appended
    $scope.injectedObject = {};

    // now i can directly calling invoke function from here 
     $scope.injectedObject.invoke();
}];

이것은 기본적으로 관심사 원칙의 분리에 위배됩니다. 컨트롤러에서 인스턴스화 된 객체를 지시어에 제공하고 해당 객체 관리 (즉, invoke 함수 생성)의 책임을 지시어에 위임합니다. 제 생각에는 더 나은 해결책이 아닙니다.
Florin Vistig
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.