Метод вызова в директивном контроллере от другого контроллера

У меня есть директива с собственным контроллером. См. Приведенный ниже код:

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

Таким образом, при вызове show в директивном контроллере popdown функция ссылки также должна запускаться и выполнять анимацию. Как я могу это достичь?

Ответ 1

Это интересный вопрос, и я начал думать о том, как бы реализовать что-то подобное.

Я придумал этот (скрипка);

В принципе, вместо того, чтобы пытаться вызвать директиву с контроллера, я создал модуль для размещения всей логики всплытия:

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

Я вложил две вещи в модуль: factory для API, который можно вставить в любом месте, и directive для определения поведения фактического элемента всплывающего окна:

factory просто определяет пару функций 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 для изменений (я использую bootstrap 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>

Я не уверен, что он полностью идеален, но казалось разумным способом настроить связь с директивой global-ish popdown.

Снова для справки скрипка.

Ответ 2

Вы также можете использовать события для запуска Popdown.

Здесь сценарий на основе решения satchmorun. Он распределяет с PopdownAPI и контроллер верхнего уровня вместо $broadcast 'success' и 'error' события вниз по цепочке видимости:

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

Это работает, по крайней мере, и кажется мне приятным решением. Я позволю другим перезвонить, если по какой-то причине это считается плохой практикой.

Ответ 3

Вы также можете выставить контроллер директивы в родительскую область, например, ngForm с атрибутом name: http://docs.angularjs.org/api/ng.directive:ngForm

Здесь вы можете найти очень простой пример того, как это может быть достигнуто http://plnkr.co/edit/Ps8OXrfpnePFvvdFgYJf?p=preview

В этом примере у меня есть myDirective с выделенным контроллером с методом $clear (вроде простого простого публичного API для директивы). Я могу опубликовать этот контроллер в родительской области и использовать этот метод вне директивы.

Ответ 4

Я получил гораздо лучшее решение.

вот моя директива, я ввел в ссылку на объект в директиве и расширил ее, добавив функцию 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>

my Controller:

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