Спосіб виклику в контролері директив від іншого контролера


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

Тому при виклику showна popdownдирективи управління, функція посилання повинне також бути запущений і виконати анімацію. Як я міг цього досягти?


Де ви розміщуєте виклик popdownдирективи на сторінці - це лише в одному місці, де мають бути доступні інші контролери, чи є кілька спливаючих вікон у різних місцях?
сачморун

мій index.html має таке: <div ng-view> </div> <div popdown> </div> в основному є лише 1 спливаючий екземпляр, як його призначено бути доступним у всьому світі.
користувач253530

1
Я думаю, ти мав намір писати popdown.show(...)замість цього, чи popdown.notify(...)не так? Інакше функція сповіщення є дещо заплутаною.
lanoxx

звідки воно походить popdown.notify? .notifiyметод, я маю на увазі
Зелений

Відповіді:


167

Це цікаве питання, і я почав думати про те, як би реалізувати щось подібне.

Я придумав це (скрипка) ;

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

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

Я помістив у модуль дві речі, a 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 (для зручності використовую cot bootstrap):

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 Ніколи не слід називати функцію в директиві поза межами директиви - це погана практика. Використання сервісу для управління глобальним станом, яке читає директива, є надто поширеним, і це правильний підхід. Більше додатків включає черги сповіщень та модальні діалоги.
Джош Девід Міллер

7
Дійсно виняткова відповідь! Такий корисний приклад для нас із jQuery та хребта
Брендон

11
Таким чином, чи можна використовувати цей модуль для створення декількох директив в одному і тому ж представленні? Як я можу викликати функцію успіху чи помилки певного екземпляра цієї директиви?
ira

3
@ira ви, можливо, можете змінити фабрику, щоб зберегти карту (або список) статусу та об’єктів повідомлення, а потім використовувати атрибут імені в директиві, щоб визначити, який елемент у списку вам потрібен. Отже, замість виклику success(msg)в html, ви б закликали sucess(name, msg)вибрати директиву з правильним іменем.
lanoxx

5
@JoshDavidMiller Чому ви вважаєте поганою практикою називати метод за директивою? Якщо директива інкапсулює якусь логіку DOM за призначенням, то, звичайно, цілком природно розкрити API, щоб контролери, які його використовують, могли використовувати необхідні методи?
Пол Тейлор

27

Ви також можете використовувати події для запуску спливаючого вікна.

Ось загадка, заснована на рішенні сачморуна. Він розподіляється з PopdownAPI, а контролер вищого рівня замість $broadcasts "успіх" і "помилка" по ланцюгу областей :

$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). У цьому вам потрібен доступ до області контролера для трансляції повідомлення. У будь-якому випадку це виглядає дуже стисло.
Джуліан

Мені це подобається більше, ніж підхід до сервісу для простих випадків використання, оскільки він зменшує складність і все ще вільно поєднаний
Патрік Фавре

11

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

Тут ви можете знайти дуже основний приклад того, як це можна досягти http://plnkr.co/edit/Ps8OXrfpnePFvvdFgYJf?p=preview

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


Для цього потрібні відносини між контролерами, правда? Оскільки ОП хотів отримати центр повідомлень, це може бути не ідеально для нього. Але було дуже приємно навчитися і вашого підходу. Це корисно у багатьох ситуаціях і, як ви сказали, кутовий сам його використовує.
fasfsfgs

Я намагаюся наслідувати приклад, який надає satchmorun. Я генерую деякий html під час виконання, але не використовую шаблон директиви. Я використовую контролер директиви, щоб вказати функцію для виклику з доданого html, але функція не викликається. В основному, у мене є така директива: direktives.directive ('abcXyz', функція ($ compile {return {restrict: 'AE', потрібно: 'ngModel', контролер: функція ($ область) {$ range.function1 = function () {..};}, мій html: "<a href="" ng-click="function1('itemtype')">
Марк

Це єдине елегантне рішення, яке може викрити директиву api, якщо директива не є однотонною! Я все ще не люблю користуватися, $scope.$parent[alias]тому що це для мене пахне, як використання внутрішніх кутових api. Але все ще не можна знайти більш елегантне рішення для не сингтонних директив. Інші варіанти, такі як трансляція подій або визначення порожнього об'єкта в батьківському контролері, для директиви api пахнуть ще більше.
Руслан Стельмаченко

3

Я отримав набагато краще рішення.

ось моя директива, я вводив посилання на об'єкт в директиві і розширював це, додаючи функцію виклику в код директиви.

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

Це в основному суперечить поділу принципів концерну. Ви надаєте директиві об'єкт, інстанціюваний у контролері, і ви делегуєте відповідальність за керування цим об'єктом (тобто створення функції виклику) директиві. На мою думку, НЕ краще рішення.
Флорін Вістіг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.