2013-10-26 3 views
7

Ich habe ein horizontales Dropdown-Menü mit AngularJS erstellt.Wie kann man mit dem Klick auf Dokumente umgehen und andere Controller mit AngularJS benachrichtigen?

Der Menübereich wird von einem Winkelregler namens menuController verwaltet. Das Standard-Menü-Verhalten ist implementiert, so dass im Hover-Hauptmenüelement hervorgehoben wird, es sei denn, es ist deaktiviert. Wenn Sie auf den Hauptmenüpunkt klicken, wird das Untermenü umgeschaltet. Wenn das Untermenü geöffnet ist, möchte ich, dass es verschwindet, wenn der Benutzer irgendwo anders auf das Dokument klickt. Ich habe versucht, eine Direktive zu erstellen, die auf das Klickereignis eines Dokuments wartet, aber nicht sicher ist, wie ich den Menücontroller darüber benachrichtigen soll. Wie soll ich dieses Szenario auf AngularJS-Basis umsetzen?

Teilweise arbeiten Original Plunk ohne Dokument Klick-Handling-Mechanismus.

UPDATE:

auf beantwortet Vorschlag Basierend, ging ich mit brodcast Ansatz und das Skript aktualisiert, um meine neuesten Änderungen widerzuspiegeln. Es funktioniert nach meiner Erwartung. Ich habe den GlobalController $ Broadcast eine Nachricht senden und MenuController diese Nachricht abonnieren.

UPDATE 2: Modifizierter Code zum Eingeben globaler Ereignisdefinitionsdaten.

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: Modified Code in früheren Implementierung einen Fehler zu beheben, wo Dokument klicken Sie mehrmals ausgelöst. Fast ähnlicher Ansatz, aber dieses Mal, wenn jemand wieder irgendwo auf dem Menü klickt, wird der Klick ignoriert. Bitte beachten Sie die New Working Plunk für die vollständige Codebeispiel

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: Implementiert eine andere alternative Option. Bitte beachten Sie die Option 2 Plunk für die vollständige Codebeispiel

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

Ich habe das oben erwähnte Plunk mit vollständig funktionierenden Beispiel aktualisiert und auch den Java-Skript-Quellcode aktualisiert, der im obigen Beitrag aufgeführt ist. – Vinod

Antwort

17

Sie die Richtlinie in etwa wie folgt vereinfachen:

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

Und dann eine Funktion aus der menuController es passieren:

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

Keine Notwendigkeit für den GlobalController auf diese Weise.

Wenn Sie die globalController behalten wollen und es von dort handhaben, können Sie:

1.) Machen Sie das Menü in einen Dienst und dann spritzen sie in alle Controller, die es kontrollieren zu können, benötigen.

2.) Senden Sie ein Ereignis von globalController und hören Sie es in menuController ab.

Spezifische alternative Lösung: Sie können die Richtlinie verwandeln sich in eine ‚on-außen-Element-Klick‘ und es wie folgt verwendet werden:

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

Die Richtlinie sieht wie folgt aus und wird closeMenus() nur anrufen, wenn Sie klicken außerhalb des 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); 
     }); 
     } 
    }; 
    } 
]); 

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

+0

Was Sie hier empfohlen haben, wird definitiv funktionieren. Ich ging mit Ihrer Option 2 Broadcast-Ansatz. Mein Gedanke war, dass nicht nur das Menü, sondern auch andere Teile der App beim Klick auf Dokumente etwas Bestimmtes tun müssen, sie können auch auf die gleiche Nachricht warten. Gleichzeitig versucht stopPropogation zu vermeiden. – Vinod

+1

Diese Lösung ist eleganter. – Vinod

1

Nun haben Sie dünn gemacht gs gut. Wenn Sie gelten die gleiche Richtlinie über die menuController

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

und haben den Handler Klick definiert in Ihrem menuController Sie alle eingestellt sind, zu gehen.

Ich glaube nicht, dass es schadet, mehrere Handler für das Ereignis auf dem Dokument zu haben. Wo immer Sie diese Direktive definieren, kann dieses Element auf das Click-Ereignis des globalen Dokuments reagieren.

Update: Wie ich dies getestet habe, führt es zu einem anderen Problem, wo diese Methode aufgerufen wird, wo immer Sie auf die Seite klicken. Sie brauchen einen Mechanismus, um jetzt zu unterscheiden.

+1

Zustimmen, versucht mit der Broadcast/on-Methode in der oben genannten Antwort in der Plunkr erwähnt und lief auf das gleiche Problem, muss in dieser Handler vor dem Senden des Ereignisses überprüfen, um zu sehen, ob das Ding geklickt war die Schaltfläche in diesem Fall sollte es nicht t das Menü ausblenden (eher der Knopf Click-Handler sollte nur das Menü umschalten) – shaunhusain

Verwandte Themen