2013-10-26 8 views
7

Utworzyłem horyzontalne menu rozwijane przy użyciu AngularJS.Jak obsługiwać klikanie dokumentów i powiadamiać inne kontrolery za pomocą AngularJS?

Sekcja menu jest zarządzana przez regulator kątowy zwany menuController. Zachowane jest standardowe zachowanie menu, więc po najechaniu głównym elementem menu zostanie podświetlony, chyba że jest wyłączony. Po kliknięciu na pozycję menu głównego następuje przełączenie podmenu. Jeśli menu podrzędne jest w stanie otwartym, chcę, aby zniknęło, gdy użytkownik kliknie nigdzie indziej w dokumencie. Próbowałem utworzyć dyrektywę, aby wykryć zdarzenie związane z kliknięciem dokumentu, ale nie wiem, jak powiadomić o tym kontroler menu. Jak powinienem wdrożyć ten scenariusz w sposób AngularJS?

Częściowo działające Original Plunk bez mechanizmu obsługi kliknięć dokumentów.

UPDATE:

odpowiedział na podstawie sugestii, poszedłem z podejściem transmitowane i zaktualizowany skrypt, aby odzwierciedlić moje najnowsze zmiany. Działa zgodnie z moimi oczekiwaniami. Sprawiłem, że globalController $ nadaje komunikat, a menuController zasubskrybuje tę wiadomość.

UPDATE 2: Zmodyfikowany kod do wprowadzania danych definicji zdarzeń globalnych.

var eventDefs = (function() { 
    return { 
    common_changenotification_on_document_click: 'common.changenotification.on.document.click' 
    }; 
}()); 

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

changeNotificationApp.value('appEvents', eventDefs); 

changeNotificationApp.directive("onGlobalClick", ['$document', '$parse', 
    function($document, $parse) { 
    return { 
     restrict: 'A', 
     link: function($scope, $element, $attributes) { 
     var scopeExpression = $attributes.onGlobalClick; 

     var invoker = $parse(scopeExpression); 

     $document.on("click", 
      function(event) { 
      $scope.$apply(function() { 
       invoker($scope, { 
       $event: event 
       }); 
      }); 
      } 
     ); 
     } 
    }; 
    } 
]); 

changeNotificationApp.controller("globalController", ['$scope', 'appEvents', 
    function($scope, appEvents) { 
    $scope.handleClick = function(event) { 
     $scope.$broadcast(appEvents.common_changenotification_on_document_click, { 
     target: event.target 
     }); 
    }; 
    } 
]); 

//menu-controller.js 
changeNotificationApp.controller('menuController', ['$scope', '$window', 'appEvents', 
    function($scope, $window, appEvents) { 

    $scope.IsLocalMenuClicked = false; 

    $scope.menu = [{ 
     Name: "INTEGRATION", 
     Tag: "integration", 
     IsDisabled: false, 
     IsSelected: false, 
     SubMenu: [{ 
     Name: "SRC Messages", 
     Tag: "ncs-notifications", 
     IsDisabled: false, 
     AspNetMvcController: "SearchSRCMessages" 
     }, { 
     Name: "Target Messages", 
     Tag: "advisor-notifications", 
     IsDisabled: false, 
     AspNetMvcController: "SearchTaregtMessages" 
     }] 
    }, { 
     Name: "AUDITING", 
     Tag: "auditing", 
     IsDisabled: true, 
     IsSelected: false, 
     SubMenu: [] 
    }]; 

    $scope.appInfo = { 
     Version: "1.0.0.0", 
     User: "VB", 
     Server: "azzcvy0623401v", 
     IsSelected: false 
    }; 

    var resetMenu = function() { 
     angular.forEach($scope.menu, function(item) { 
     item.IsSelected = false; 
     }); 
     $scope.appInfo.IsSelected = false; 
    }; 

    $scope.toggleDropDownMenu = function(menuItem) { 
     var currentDropDownState = menuItem.IsSelected; 
     resetMenu($scope.menu, $scope.appInfo); 
     menuItem.IsSelected = !currentDropDownState; 
     $scope.IsLocalMenuClicked = true; 
    }; 

    $scope.loadPage = function(menuItem) { 
     if (menuItem.AspNetMvcController) 
     $window.location.href = menuItem.AspNetMvcController; 
    }; 

    $scope.$on(appEvents.common_changenotification_on_document_click, 
     function(event, data) { 
     if (!$scope.IsLocalMenuClicked) 
      resetMenu($scope.menu, $scope.appInfo); 
     $scope.IsLocalMenuClicked = false; 
     }); 
    } 
]); 

UPDATE 3: Zmodyfikowany kod w poprzedniej realizacji, aby naprawić błąd, gdzie dokument kliknij odpala wielokrotnie. Prawie podobne podejście, ale tym razem, jeśli ktoś kliknie ponownie w dowolnym miejscu menu, kliknięcie jest ignorowane. Proszę odnieść się do New Working Plunk pełną przykład kodu

changeNotificationApp.directive("onGlobalClick", ['$document', '$parse', 
    function ($document, $parse) { 
     return { 
      restrict: 'A', 
      link: function ($scope, $element, $attributes) { 
       var scopeExpression = $attributes.onGlobalClick; 

       var invoker = $parse(scopeExpression); 

       $document.on("click", 
        function (event) { 
         var isClickedElementIsChildOfThisElement = $element.find(event.target).length > 0; 
          if (isClickedElementIsChildOfThisElement) return; 
         $scope.$apply(function() { 
          invoker($scope, { 
           $event: event 
          }); 
         }); 
        } 
       ); 
      } 
     }; 
    } 
]); 

UPDATE 4: Zaimplementowane inną opcję alternatywną. Proszę odnieść się do Option 2 Plunk pełną przykład kodu

var eventDefs = (function() { 
    return { 
     on_click_anywhere: 'common.changenotification.on.document.click' 
    }; 
}()); 

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

changeNotificationApp.value('appEvents', eventDefs); 

changeNotificationApp.directive("onClickAnywhere", ['$window', 'appEvents', 
    function($window, appEvents) { 
    return { 
     link: function($scope, $element) { 
     angular.element($window).on('click', function(e) { 
      // Namespacing events with name of directive + event to avoid collisions 
      $scope.$broadcast(appEvents.on_click_anywhere, e.target); 
     }); 
     } 
    }; 
    } 
]); 

//menu-controller.js 
changeNotificationApp.controller('menuController', ['$scope', '$window', 'appEvents', '$element', 
    function ($scope, $window, appEvents, $element) { 

     $scope.menu = [ 
      { 
       Name: "INTEGRATION", 
       Tag: "integration", 
       IsDisabled: false, 
       IsSelected: false, 
       SubMenu: [ 
        { 
         Name: "SRC Messages", 
         Tag: "ncs-notifications", 
         IsDisabled: false, 
         AspNetMvcController: "SearchSRCMessages" 
        }, 
        { 
         Name: "Target Messages", 
         Tag: "advisor-notifications", 
         IsDisabled: false, 
         AspNetMvcController: "SearchTaregtMessages" 
        } 
       ] 
      }, 
      { 
       Name: "AUDITING", 
       Tag: "auditing", 
       IsDisabled: true, 
       IsSelected: false, 
       SubMenu: [] 
      } 
     ]; 

     $scope.appInfo = { 
      Version: "1.0.0.0", 
      User: "VB", 
      Server: "azzcvy0623401v", 
      IsSelected: false 
     }; 

     var resetMenu = function() { 
      angular.forEach($scope.menu, function (item) { 
       item.IsSelected = false; 
      }); 
      $scope.appInfo.IsSelected = false; 
     }; 

     $scope.toggleDropDownMenu = function (menuItem) { 
      var currentDropDownState = menuItem.IsSelected; 
      resetMenu($scope.menu, $scope.appInfo); 
      menuItem.IsSelected = !currentDropDownState; 
     }; 

     $scope.loadPage = function (menuItem) { 
      if (menuItem.AspNetMvcController) 
       $window.location.href = menuItem.AspNetMvcController; 
     }; 

     $scope.$on(appEvents.on_click_anywhere, function(event, targetElement) { 
      var isClickedElementIsChildOfThisElement = $element.find(targetElement).length > 0; 
      if (isClickedElementIsChildOfThisElement) return; 

      $scope.$apply(function(){ 
      resetMenu($scope.menu, $scope.appInfo); 
      }); 
     }); 
    } 
]); 
+0

Zaktualizowałem wspomniany plunk przy użyciu w pełni działającej próbki, a także zaktualizowałem kod źródłowy skryptu java wymieniony w powyższym wpisie. – Vinod

Odpowiedz

17

Można uprościć dyrektywy do mniej więcej tak:

changeNotificationApp.directive('onDocumentClick', ['$document', 
    function($document) { 
    return { 
     restrict: 'A', 
     link: function(scope, element, attrs) { 

     var onClick = function() { 
      scope.$apply(function() { 
      scope.$eval(attrs.onDocumentClick); 
      }); 
     }; 

     $document.on('click', onClick); 

     scope.$on('$destroy', function() { 
      $document.off('click', onClick); 
     }); 
     } 
    }; 
    } 
]); 

a następnie przekazać funkcję z menuController do niego:

<section class="local-nav" ng-controller="menuController" on-document-click="someFunction()"> 

Nie ma potrzeby korzystania z globalController w ten sposób.

Jeśli chcesz zachować globalController i obsługiwać go stamtąd można:

1.) Dokonać menu do serwisu, a następnie wstrzyknąć go do wszystkich sterowników, które muszą być w stanie go kontrolować.

2.) Rozsyłanie zdarzenia z globalController i odsłuchiwanie go w menuController.

Szczególne rozwiązanie alternatywne: Można włączyć dyrektywy na „on-outside-elementu-click” i używać go tak:

<ul on-outside-element-click="closeMenus()"> 

Dyrektywa wygląda i będzie tylko zadzwonić closeMenus() jeśli klikniesz poza ul:

changeNotificationApp.directive('onOutsideElementClick', ['$document', 
    function($document) { 
    return { 
     restrict: 'A', 
     link: function(scope, element, attrs) { 

     element.on('click', function(e) { 
      e.stopPropagation(); 
     }); 

     var onClick = function() { 
      scope.$apply(function() { 
      scope.$eval(attrs.onOutsideElementClick); 
      }); 
     }; 

     $document.on('click', onClick); 

     scope.$on('$destroy', function() { 
      $document.off('click', onClick); 
     }); 
     } 
    }; 
    } 
]); 

roboczej Plunker: http://plnkr.co/edit/zVo0fL2wOCQb3eAUx44U?p=preview

+0

To, co tutaj poleciłeś, z pewnością zadziała. Wybrałem opcję emisji 2. Myślałem, że nie tylko menu, ale jeśli inne części aplikacji muszą zrobić coś konkretnego na kliknięcie dokumentu, mogą również słuchać tej samej wiadomości. Jednocześnie próbuje uniknąć stopPropogation. – Vinod

+1

To rozwiązanie jest bardziej eleganckie. – Vinod

1

Dobrze zrobiliście cienki gs dobrze. Jeśli stosuje się taką samą dyrektywę nad menuController

<section class="local-nav" ng-controller="menuController" on-global-click="handleClick($event)> 

i mają obsługi kliknij zdefiniowanych w menuController masz wszystko ustawione, aby przejść.

Nie sądzę, że istnieje jakakolwiek szkoda w posiadaniu wielu procedur obsługi zdarzenia na dokumencie. Jeśli kiedykolwiek zdefiniujesz tę dyrektywę, element ten może odpowiedzieć na zdarzenie globalnego kliknięcia dokumentu.

Aktualizacja: Jak to przetestowałem, prowadzi to do innego problemu, w którym ta metoda jest wywoływana, gdziekolwiek klikasz na stronie. Potrzebujesz mechanizmu do rozróżnienia teraz.

+1

Zgadzam się, próbowałem użyć metody broadcast/on wspomnianej w powyższej odpowiedzi w plunkr i wpadłem na ten sam problem, muszę sprawdzić w tym handlercie przed transmisją zdarzenia, aby zobaczyć, czy rzeczą kliknięto był przycisk, w którym to przypadku nie powinien " • ukryj menu (raczej przyciski obsługi kliknięć powinny tylko przełączać menu) – shaunhusain

Powiązane problemy