2013-07-17 5 views
26

Jest to kontynuacja pytanie na to pytanie: AngularJS input with focus kills ng-repeat filter of listNie wiem, jak ukryć div po kliknięciu poza div

Zasadniczo mój kod używa angularjs pop-out div (szuflada) na prawo do filtrowania listy rzeczy. W większości przypadków jest używany blokowany interfejs użytkownika, więc kliknięcie tego bloku blokującego zamyka szufladę. Jednak w niektórych przypadkach nie blokujemy interfejsu użytkownika i musimy pozwolić użytkownikowi na kliknięcie poza szufladą, aby anulować wyszukiwanie lub wybrać coś innego na stronie.

Moja początkowa myśl polegała na tym, aby dołączyć szufladkę window.onclick, gdy szuflada się otworzy i jeśli cokolwiek zostanie kliknięte poza szufladą, należy zamknąć szufladę. Tak właśnie zrobiłbym w czystej aplikacji JavaScript. Ale w Angularie jest to trochę trudniejsze.

Oto jsfiddle z próbką, że w oparciu o @ jsBin przykład Yoshi: http://jsfiddle.net/tpeiffer/kDmn8/

Odpowiedni fragment kodu z tej próbki jest poniżej. Zasadniczo, jeśli użytkownik kliknie poza szufladę, wywołuję $ scope.toggleSearch, aby $ scope.open powrócił do wartości false.

Kod działa, i chociaż $ scope.open przechodzi od wartości true do false, nie modyfikuje DOM. Jestem pewien, że ma to coś wspólnego z cyklem życia wydarzeń lub kiedy modyfikuję $ scope (ponieważ pochodzi z osobnego wydarzenia), że jest to kopia, a nie oryginał ...

Ostatecznym celem będzie to stanowić dyrektywę o ostatecznym wykorzystaniu. Jeśli ktokolwiek może wskazać mi właściwy kierunek, będę wdzięczny.

$scope.toggleSearch = function() { 

    $scope.open = !$scope.open; 

    if ($scope.open) { 
     $scope.$window.onclick = function (event) { 
      closeSearchWhenClickingElsewhere(event, $scope.toggleSearch); 
     }; 
    } else { 
     $scope.$window.onclick = null; 
    } 
}; 

i

function closeSearchWhenClickingElsewhere(event, callbackOnClose) { 

    var clickedElement = event.target; 
    if (!clickedElement) return; 

    var elementClasses = clickedElement.classList; 
    var clickedOnSearchDrawer = elementClasses.contains('handle-right') 
     || elementClasses.contains('drawer-right') 
     || (clickedElement.parentElement !== null 
      && clickedElement.parentElement.classList.contains('drawer-right')); 
    if (!clickedOnSearchDrawer) { 
     callbackOnClose(); 
    } 

} 

Odpowiedz

21

Szuflada nie zamyka, ponieważ zdarzenie click występuje poza cyklem strawienia i kątowe nie wie, że $ scope.open uległ zmianie. Aby to naprawić, możesz wywołać $ scope. $ Apply() po ustawieniu opcji $ scope.open na wartość false, spowoduje to rozpoczęcie cyklu trawienia.

$scope.toggleSearch = function() { 
    $scope.open = !$scope.open; 
    if ($scope.open) { 
     $scope.$window.onclick = function (event) { 
      closeSearchWhenClickingElsewhere(event, $scope.toggleSearch); 
     }; 
    } else { 
     $scope.open = false; 
     $scope.$window.onclick = null; 
     $scope.$apply(); //--> trigger digest cycle and make angular aware. 
    } 
}; 

Oto Twoja Fiddle.

Próbowałem także utworzyć dyrektywę dla szuflady wyszukiwania, jeśli to pomaga (wymaga pewnych poprawek :)). Oto JSBin.

+1

Dziękuję bardzo @Bertrand! Ten $ apply był dokładnie tym, czego mi brakowało! Jestem w piątym dniu korzystania z Angular (profesjonalnie) - wciąż kilka kluczowych koncepcji, które zabiorą mi trochę czasu na zapoznanie się! Mam nadzieję, że zwrócę przysługę w najbliższej przyszłości! Sprawdzę JSBin pod kątem dyrektywy i opublikuję wyniki, gdy już to zrobię i działam. –

2

nie mogłem znaleźć rozwiązanie byłem w 100% zadowolony z tego, co kiedyś:

<div class="options"> 
    <span ng-click="toggleAccountMenu($event)">{{ email }}</span> 
    <div ng-show="accountMenu" class="accountMenu"> 
     <a ng-click="go('account')">Account</a> 
     <a ng-click="go('logout')">Log Out</a> 
    </div> 
</div> 

rozpiętość z NG-kliknięcie służy do otwierania menu, na div.accountMenu jest przełączana otwarte lub zamknięte

To wykorzystuje jQuery do sprawdzania dzieci, ale prawdopodobnie można to zrobić bez potrzeby.

ja dostawałem jakieś paskudne błędy z wersji innych ludzi, takich jak próby wywołania $ apply(), gdy jej już w cyklu, moja wersja uniemożliwia rozmnażanie i bezpiecznych kontrole przeciwko $ apply()

12

Proponuję dodaj $ event.stopPropagation(); w widoku zaraz po ng-kliknięciu. Nie musisz używać jQuery.

<button ng-click="toggleSearch();$event.stopPropagation();">toggle</button> 

Następnie można użyć tego uproszczonego js.

$scope.toggleSearch = function() { 
    $window.onclick = null; 
    $scope.menuOpen = !$scope.menuOpen; 

    if ($scope.model.menuOpen) { 
     $window.onclick = function (event) { 
      $scope.menuOpen = false; 
      $scope.$apply(); 
     }; 
    } 
}; 
3

Zaakceptowanych odpowiedź wygeneruje błąd, jeśli klikniesz na przycisk w celu zamknięcia szuflady/popup, a przycisk znajduje się poza nim, bo $apply() będą realizowane dwa razy.

Jest to wersja uproszczona, która nie musi ponownie wywoływać całej funkcji toggleSearch() i zapobiega podwójnemu $apply().

$scope.toggleSearch = function() { 

    $scope.open = !$scope.open; 

    if ($scope.open) { 
    $window.onclick = function(event) { 
     var clickedElement = event.target; 
     if (!clickedElement) return; 

     var clickedOnSearchDrawer = elementClasses.contains('handle-right') || elementClasses.contains('drawer-right') || (clickedElement.parentElement !== null && clickedElement.parentElement.classList.contains('drawer-right')); 

     if (!clickedOnSearchDrawer) { 
     $scope.open = !$scope.open; 
     $window.onclick = null; 
     $scope.$apply(); 
     } 
    } 
    } else { 
    $window.onclick = null; 
    } 
};