2013-10-26 6 views
7

AngularJSを使用して水平ドロップダウンメニューを作成しました。AngularJSを使用してドキュメントをクリックして他のコントローラに通知する方法は?

メニューセクションは、menuControllerという角度コントローラーによって管理されます。標準メニューの動作が実装されているため、ホバーオンのメインメニュー項目は無効にされていない限り強調表示されます。メインメニュー項目をクリックすると、サブメニューが切り替わります。サブメニューが開いている状態であれば、ユーザーがドキュメント上の他の場所をクリックすると、それが消えてしまいます。私は、ドキュメントのクリックイベントを聞くためのディレクティブを作成しようとしましたが、メニューコントローラに通知する方法は不明です。このシナリオをAngularJSの方法で実装するにはどうすればよいですか?

部分的に動作するOriginal Plunkドキュメントのクリック処理メカニズムなし。

UPDATE:答えの提案に基づいて

、私はBrodcastアプローチを行って、私の最新の変更を反映するために、スクリプトを更新しました。私の期待通りに働いています。私はglobalController $をメッセージに、menuControllerをそのメッセージに登録しました。

更新2:グローバルイベント定義データを挿入する修正コード。

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:文書のクリックが複数回発射するバグを修正する以前の実装にコードを修正しました。ほぼ同様のアプローチですが、今回は、メニューのどこかをクリックすると、そのクリックは無視されます。別の代替オプションを実装:UPDATE 4

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

完全なコード例えばNew Working Plunkを参照してください。

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

そしてmenuControllerから、それに関数を渡す:あなたはこのような何かにディレクティブを簡素化することができ、完全なコード例

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

上記のplunkを完全に動作するサンプルで更新し、上記のポストに記載されているjavaスクリプトのソースコードも更新しました。 – Vinod

答えて

17

ためOption 2 Plunkを参照してください

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

このようにglobalControllerは必要ありません。

あなたがglobalControllerを維持し、そこからそれを処理したい場合は、あなたがすることができます

1)はサービスにメニューを作成し、それを制御できるようにする必要があり、すべてのコントローラに注入。

2.)globalControllerからイベントをブロードキャストし、それをmenuControllerでリッスンします。

具体的な代替ソリューション:ディレクティブは次のようになり、あなたがクリックした場合のみcloseMenus()を呼び出します

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

:あなたは「オン・外要素-クリック」にディレクティブをオンにし、このようにそれを使用することができます

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

ワーキングPlunker:http://plnkr.co/edit/zVo0fL2wOCQb3eAUx44U?p=preview

+0

あなたがここでお勧めしたことは間違いなく機能します。私はあなたのオプション2の放送アプローチに行った。私の考えは、メニューだけでなく、アプリの他の部分がドキュメントのクリックで特定のことをしなければならない場合、同じメッセージを聞くこともできます。同時に、stopPropogationを回避しようとしています。 – Vinod

+1

このソリューションはより洗練されています。 – Vinod

1

さてあなたは薄い行っているul外うーん。あなたはmenuController

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

以上同じディレクティブを適用すると、あなたのmenuControllerで定義されたクリックハンドラを持っている場合、あなたはすべて行くように設定されています。

ドキュメント上のイベントに対して複数のハンドラを持つことに害はないとは思いません。このディレクティブをどこで定義すれば、その要素はグローバルドキュメントのクリックイベントに応答できます。

更新::これをテストしたところ、このメソッドが呼び出される別の問題が発生しました今すぐ差別化するための仕組みが必要です。

+1

同意し、上記のplunkrの答えに記載されているブロードキャスト/メソッドを使用しようとしましたが、同じ問題が発生しました。イベントをブロードキャストする前にそのハンドラをチェックして、クリックされたものがボタンかどうかを確認する必要があります。 tメニューを非表示にする(むしろボタンのクリックハンドラはメニューをトグルするだけでよい) – shaunhusain

関連する問題