2013-02-14 1 views
117

У меня есть директива с собственным контроллером. См. Код ниже:Метод вызова в директивном контроллере от другого контроллера

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 директивы, функция ссылка должна также быть запущен и выполнить анимацию. Как я мог это достичь?

+0

Где вы ставите вызов на 'popdown' директивы на странице - это это просто в одном месте, где другие контроллеры должны иметь доступ к нему, или есть несколько выходов в разных местах? – satchmorun

+0

мой index.html имеет это:

в основном есть только один экземпляр в раскрывающемся меню, как его означало быть доступны по всему миру. – user253530

+1

Я думаю, вы хотели написать 'popdown.show (...)' вместо 'popdown.notify (...)' это правильно? В противном случае функция уведомления является путаницей. – lanoxx

ответ

166

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

Я придумал this (fiddle);

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

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> 

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

Опять же, для справки, the fiddle.

+9

+1. Никогда не следует вызывать функцию в директиве извне директивы - это плохая практика. Использование службы для управления глобальным состоянием, которое читает директива, является супер общим, и это правильный подход. Другие приложения включают очереди уведомлений и модальные диалоги. –

+2

Отличный ответ! Вы ответили на этот вопрос и все мои другие вопросы о том, как писать хорошие модули для повторного использования, которые можно просто удалить в любом другом проекте и использовать как есть. Спасибо огромное! Я искал эту информацию за последние 2 дня и просто не мог найти ничего, чтобы ответить на мои вопросы. – user253530

+6

Действительно исключительный ответ! Такой полезный пример для тех из нас, которые поступают из jQuery и Backbone – Brandon

26

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

Here's a fiddle на основе решения satchmorun. Он дозирует с PopdownAPI и контроллером верхнего уровня вместо $broadcast S «успех» и события «ошибка» вниз по цепочке областей видимости:

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

Модуль регистрирует в раскрывающемся меню функции обработчика для этих событий, е.g:

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

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

+1

Один из недостатков, о котором я могу думать, заключается в том, что в выбранном ответе вам нужен только PopdownAPI (доступный с DI). В этом вам нужен доступ к области контроллера для трансляции сообщения. Во всяком случае, это выглядит очень кратким. – Julian

+0

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

10

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

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

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

+0

Для этого нужны отношения между контроллерами, не так ли? Поскольку OP хотел центр сообщений, это может быть не идеальным для него. Но было очень приятно узнать ваш подход. Это полезно во многих ситуациях, и, как вы сказали, угловой сам использует его. – fasfsfgs

+0

Я пытаюсь следовать примеру, предоставленному satchmorun. Я генерирую некоторый html во время выполнения, но я не использую шаблон директивы. Я использую контроллер директивы, чтобы указать функцию для вызова из добавленного html, но функция не вызвана. В принципе, у меня есть эта директива: directives.directive ('abcXyz', функция ($ компилировать { возвращения { ограничение: 'AE', требует: 'ngModel', контроллера: функция ($ сфера) {$ сферы. function1 = function() { .. }; }, мой html is: " Mark

+0

Это единственное изящное решение, которое может выставить директиву api, если директива не является одиночной! Мне все еще не нравится использовать '$ scope. $ parent [alias]', потому что это пахнет для меня, как использование внутреннего углового api. Но все же не может найти более элегантное решение для не-singleton Другие варианты, такие как трансляция событий или определение пустого объекта в родительском контроллере для директивы api, еще больше пахнут. – djxak

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

Этот basicall y идет вразрез с принципами разделения интересов.Вы предоставляете директиве объект, созданный в контроллере, и делегируете ответственность за управление этим объектом (т. Е. Создание функции вызова) в директиве. По-моему, НЕ лучшее решение. –

 Смежные вопросы

  • Нет связанных вопросов^_^