Ich möchte mein Dropdown-Menü für das Anmeldemenü schließen, wenn der Benutzer auf eine beliebige Stelle außerhalb dieses Dropdowns klickt, und ich möchte dies mit Angular2 und dem Angular2-Ansatz tun ...
Ich habe eine Lösung implementiert, bin aber nicht sicher. Ich denke, es muss einen einfachsten Weg geben, um das gleiche Ergebnis zu erzielen. Wenn Sie also Ideen haben ... lassen Sie uns darüber diskutieren :)!
Hier ist meine Implementierung:
Die Dropdown-Komponente:
Dies ist die Komponente für mein Dropdown:
- Jedes Mal, wenn diese Komponente sichtbar wird (z. B. wenn der Benutzer auf eine Schaltfläche klickt, um sie anzuzeigen), abonniert sie ein "globales" rxjs-Betreff userMenu, das im SubjectsService gespeichert ist .
- Und jedes Mal, wenn es versteckt ist, wird dieses Thema abgemeldet.
- Jeder Klick irgendwo in der Vorlage dieser Komponente löst die onClick () -Methode aus, die einfach verhindert, dass das Ereignis nach oben sprudelt (und die Anwendungskomponente).
Hier ist der Code
export class UserMenuComponent {
_isVisible: boolean = false;
_subscriptions: Subscription<any> = null;
constructor(public subjects: SubjectsService) {
}
onClick(event) {
event.stopPropagation();
}
set isVisible(v) {
if( v ){
setTimeout( () => {
this._subscriptions = this.subjects.userMenu.subscribe((e) => {
this.isVisible = false;
})
}, 0);
} else {
this._subscriptions.unsubscribe();
}
this._isVisible = v;
}
get isVisible() {
return this._isVisible;
}
}
Die Anwendungskomponente:
Auf der anderen Seite gibt es die Anwendungskomponente (die der Dropdown-Komponente übergeordnet ist):
- Diese Komponente fängt jedes Klickereignis ab und sendet auf demselben rxjs-Betreff ( userMenu ).
Hier ist der Code:
export class AppComponent {
constructor( public subjects: SubjectsService) {
document.addEventListener('click', () => this.onClick());
}
onClick( ) {
this.subjects.userMenu.next({});
}
}
Was mich stört:
- Ich fühle mich nicht wirklich wohl mit der Idee, ein globales Thema zu haben, das als Bindeglied zwischen diesen Komponenten fungiert.
- Das setTimeout : Dies ist erforderlich, da Folgendes passiert, wenn der Benutzer auf die Schaltfläche klickt, die das Dropdown-Menü anzeigt:
- Der Benutzer klickt auf die Schaltfläche (die nicht Teil der Dropdown-Komponente ist), um die Dropdown-Liste anzuzeigen.
- Das Dropdown-Menü wird angezeigt und abonniert sofort den Betreff userMenu .
- Das Klickereignis sprudelt zur App-Komponente und wird abgefangen
- Die Anwendungskomponente emittieren , ein Ereignis auf dem UserMenu Subjekt
- Die Dropdown-Komponente fängt diese Aktion im Benutzermenü ab und blendet das Dropdown aus.
- Am Ende wird das Dropdown nie angezeigt.
Diese festgelegte Zeitüberschreitung verzögert das Abonnement bis zum Ende der aktuellen JavaScript-Code-Runde, wodurch das Problem gelöst wird, aber meiner Meinung nach auf sehr elegante Weise.
Wenn Sie sauberere, bessere, intelligentere, schnellere oder stärkere Lösungen kennen, lassen Sie es mich bitte wissen :)!
quelle
Antworten:
Sie können
(document:click)
event verwenden:Ein anderer Ansatz besteht darin, ein benutzerdefiniertes Ereignis als Direktive zu erstellen. Schauen Sie sich diese Beiträge von Ben Nadel an:
quelle
@HostListener('document:click', ['$event'])
anstelle vonhost
Eigentum denComponent
Dekorateur verwenden.Observable.fromEvent(document, 'click').subscribe(event => {your code here})
, so dass Sie immer nur abonnieren können, wenn Sie zuhören müssen, zum Beispiel wenn Sie Dropdown geöffnet haben, und wenn Sie es schließen, melden Sie sich abELEGANTE METHODE
Ich habe diese
clickOut
Anweisung gefunden: https://github.com/chliebel/angular2-click-outside . Ich überprüfe es und es funktioniert gut (ich kopiere nurclickOutside.directive.ts
in mein Projekt). Sie können es folgendermaßen verwenden:Wo
close
ist Ihre Funktion, die aufgerufen wird, wenn der Benutzer außerhalb von div klickt? Es ist eine sehr elegante Art, mit dem fraglichen Problem umzugehen.Wenn Sie die obige Anweisung verwenden, um das PopUp-Fenster zu schließen, müssen Sie zuerst hinzufügen
event.stopPropagation()
Ereignishandler für Schaltflächenklicks , der PopUp öffnet.BONUS:
Unten kopiere ich den Code der oryginalen Direktive aus der Datei
clickOutside.directive.ts
(falls der Link in Zukunft nicht mehr funktioniert) - der Autor ist Christian Liebel :Code-Snippet anzeigen
quelle
<div class="wrap" *ngIf="isOpened" (clickOutside)="...// this should set this.isOpen=false"
Ich habe es so gemacht.
Es wurde ein Ereignis-Listener zum Dokument hinzugefügt
click
und in diesem Handler überprüft, ob meincontainer
enthältevent.target
, wenn nicht - das Dropdown-Menü ausblenden.Es würde so aussehen.
quelle
this.offClickHandler
in eine Pfeilfunktion einwickelt .Ich denke, Sasxa akzeptierte Antwort funktioniert für die meisten Menschen. Ich hatte jedoch eine Situation, in der sich der Inhalt des Elements, das Off-Click-Ereignisse abhören sollte, dynamisch änderte. Das nativeElement Elements enthielt also nicht das event.target, als es dynamisch erstellt wurde. Ich könnte dies mit der folgenden Anweisung lösen
Anstatt zu überprüfen, ob elementRef event.target enthält, überprüfe ich, ob sich elementRef im Pfad (DOM-Pfad zum Ziel) des Ereignisses befindet. Auf diese Weise können dynamisch erstellte Elemente verarbeitet werden.
quelle
Wenn Sie dies unter iOS tun, verwenden Sie die
touchstart
Ereignis auch:Ab Winkel 4 ist das
HostListener
Dekorieren der bevorzugte Weg, dies zu tunquelle
Wir haben heute an einem ähnlichen Problem gearbeitet und versucht herauszufinden, wie ein Dropdown-Div verschwinden kann, wenn es angeklickt wird. Unsere Frage unterscheidet sich geringfügig von der Frage des ursprünglichen Posters, da wir nicht von einer anderen Komponente oder Direktive wegklicken wollten , sondern nur außerhalb des jeweiligen Div.
Wir haben es schließlich mithilfe des Ereignishandlers (window: mouseup) gelöst.
Schritte:
1.) Wir haben dem gesamten Dropdown-Menü div einen eindeutigen Klassennamen gegeben.
2.) Im inneren Dropdown-Menü selbst (der einzige Teil, für den Klicks das Menü NICHT schließen sollten) haben wir einen Ereignishandler (Fenster: mouseup) hinzugefügt und das $ -Ereignis übergeben.
HINWEIS: Dies konnte mit einem typischen "Klick" -Handler nicht durchgeführt werden, da dies mit dem übergeordneten Klick-Handler in Konflikt stand.
3.) In unserem Controller haben wir die Methode erstellt, die für das Click-out-Ereignis aufgerufen werden soll, und wir verwenden event.closest ( Dokumente hier ), um herauszufinden, ob sich der angeklickte Punkt innerhalb unserer Zielklasse div befindet.
quelle
.closest()
wird von IE / Edge ab heute nicht unterstützt ( caniuse )Sie können ein Geschwisterelement in der Dropdown-Liste erstellen, das den gesamten Bildschirm abdeckt, der unsichtbar ist und nur zum Erfassen von Klickereignissen dient. Dann können Sie Klicks auf dieses Element erkennen und die Dropdown-Liste schließen, wenn Sie darauf klicken. Nehmen wir an, das Element ist von der Klasse Siebdruck, hier ist ein Stil dafür:
Der Z-Index muss hoch genug sein, um ihn über allem außer Ihrem Dropdown zu positionieren. In diesem Fall wäre mein Dropdown b z-Index 2.
Die anderen Antworten funktionierten in einigen Fällen für mich, außer dass manchmal mein Dropdown geschlossen wurde, wenn ich mit Elementen darin interagierte und das nicht wollte. Ich hatte dynamisch Elemente hinzugefügt, die nicht in meiner Komponente enthalten waren, je nach Ereignisziel, wie ich erwartet hatte. Anstatt das Durcheinander zu beseitigen, dachte ich, ich würde es einfach auf Siebdruck versuchen.
quelle
Ich habe keine Problemumgehung vorgenommen. Ich habe gerade ein Dokument angehängt: Klicken Sie wie folgt auf meine Umschaltfunktion:
Wenn ich mich außerhalb meiner Richtlinie befinde, schließe ich die Dropdown-Liste.
quelle
DOWNSIDES: - Zwei Klickereignis-Listener für jede dieser Komponenten auf Seite. Verwenden Sie dies nicht für Komponenten, die sich hunderte Male auf der Seite befinden.
quelle
Ich möchte die @ Tony-Antwort ergänzen, da das Ereignis nach dem Klicken außerhalb der Komponente nicht entfernt wird. Vollständiger Beleg:
Markieren Sie Ihr Hauptelement mit #container
Verwenden Sie für das anklickbare Element:
Jetzt können Sie Ihren Dropdown-Status mit der Variablen dropstatus steuern und mit [ngClass] die richtigen Klassen anwenden ...
quelle
Sie können Direktive schreiben:
In Ihrer Komponente:
Diese Anweisung gibt ein Ereignis aus, wenn das HTML-Element in DOM enthalten ist und wenn die Eingabeeigenschaft [clickOut] 'true' ist. Es hört das Mousedown-Ereignis ab, um das Ereignis zu behandeln, bevor das Element aus dem DOM entfernt wird.
Und ein Hinweis: Firefox enthält keine Eigenschaft 'Pfad' für den Fall, dass Sie die Funktion zum Erstellen eines Pfads verwenden können:
Daher sollten Sie den Ereignishandler in der Direktive ändern: event.path sollte durch getEventPath (event) ersetzt werden.
Dieses Modul kann helfen. https://www.npmjs.com/package/ngx-clickout Es enthält dieselbe Logik, behandelt aber auch das esc-Ereignis im Quell-HTML-Element.
quelle
Die richtige Antwort hat ein Problem. Wenn Sie eine Clicakble-Komponente in Ihrem Popover haben, wird das Element nicht mehr in der
contain
Methode angezeigt und geschlossen, basierend auf @ JuHarm89, das ich selbst erstellt habe:Danke für die Hilfe!
quelle
Eine bessere Version für @ Tony großartige Lösung:
In einer CSS-Datei: // NICHT erforderlich, wenn Sie das Bootstrap-Dropdown verwenden.
quelle
Sie sollten überprüfen, ob Sie stattdessen viel einfacher auf das modale Overlay klicken.
Ihre Vorlage:
Und die Methode:
quelle
Ich habe eine Anweisung zur Behebung dieses ähnlichen Problems erstellt und verwende Bootstrap. Aber in meinem Fall, anstatt darauf zu warten, dass das Klickereignis außerhalb des Elements das aktuell geöffnete Dropdown-Menü schließt, ist es meiner Meinung nach besser, wenn wir über das Ereignis 'Mouseleave' wachen, um das Menü automatisch zu schließen.
Hier ist meine Lösung:
Richtlinie
HTML
quelle
Wenn Sie Bootstrap verwenden, können Sie dies direkt mit Bootstrap über Dropdowns (Bootstrap-Komponente) tun.
Jetzt ist es in Ordnung,
(click)="clickButton()"
Dinge auf den Knopf zu legen . http://getbootstrap.com/javascript/#dropdownsquelle
Ich habe auch selbst eine kleine Problemumgehung durchgeführt.
Ich habe ein (dropdownOpen) erstellt , das ich an meiner ng-select-Elementkomponente abhöre und eine Funktion aufrufe, die alle anderen geöffneten SelectComponent-Elemente außer der aktuell geöffneten SelectComponent schließt.
Ich habe eine Funktion in der Datei select.ts wie folgt geändert , um das Ereignis auszulösen :
Im HTML habe ich einen Ereignis-Listener für (dropdownOpened) hinzugefügt :
Dies ist meine aufrufende Funktion beim Ereignisauslöser in der Komponente mit dem Tag ng2-select:
quelle
HINWEIS: Für diejenigen, die Web-Worker verwenden möchten und die Verwendung von document und nativeElement vermeiden müssen, funktioniert dies.
Ich habe die gleiche Frage hier beantwortet: /programming/47571144
Kopieren / Einfügen über den obigen Link:
Ich hatte das gleiche Problem, als ich ein Dropdown-Menü und einen Bestätigungsdialog erstellte. Ich wollte sie schließen, wenn ich nach draußen klickte.
Meine endgültige Implementierung funktioniert perfekt, erfordert jedoch einige CSS3-Animationen und -Styling.
HINWEIS : Ich habe den folgenden Code nicht getestet. Möglicherweise müssen einige Syntaxprobleme behoben werden, auch die offensichtlichen Anpassungen für Ihr eigenes Projekt!
Was ich getan habe:
Ich habe ein separates festes Div mit Höhe 100%, Breite 100% und Transformation erstellt: scale (0), dies ist im Wesentlichen der Hintergrund, Sie können es mit der Hintergrundfarbe stylen: rgba (0, 0, 0, 0,466); Um dies zu verdeutlichen, ist das Menü geöffnet und der Hintergrund ist Click-to-Close. Das Menü erhält einen höheren Z-Index als alles andere, dann erhält das Hintergrund-Div einen niedrigeren Z-Index als das Menü, aber auch einen höheren Wert als alles andere. Dann hat der Hintergrund ein Klickereignis, das das Dropdown-Menü schließt.
Hier ist es mit Ihrem HTML-Code.
Hier ist das CSS3, das einige einfache Animationen benötigt.
Wenn Sie nichts Visuelles suchen, können Sie einfach einen solchen Übergang verwenden
quelle
Ich bin auf eine andere Lösung gestoßen, die von Beispielen mit Fokus- / Unschärfeereignis inspiriert wurde.
Wenn Sie also dieselbe Funktionalität erreichen möchten, ohne den globalen Listener für Dokumente anzuhängen, können Sie das folgende Beispiel als gültig betrachten. Es funktioniert auch in Safari und Firefox unter OSx, obwohl sie andere Behandlung von Schaltflächenfokusereignissen haben: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus
Arbeitsbeispiel für Stackbiz mit Winkel 8: https://stackblitz.com/edit/angular-sv4tbi?file=src%2Ftoggle-dropdown%2Ftoggle-dropdown.directive.ts
HTML-Markup:
Die Richtlinie sieht folgendermaßen aus:
quelle
Sie können
mouseleave
in Ihrer Ansicht so verwendenTesten Sie mit Winkel 8 und arbeiten Sie perfekt
quelle
DIE ELEGANTSTE METHODE: D.
Es gibt einen einfachsten Weg, dies zu tun, dafür sind keine Anweisungen erforderlich.
"Element-das-umschalten-dein-Dropdown" sollte Button-Tag sein. Verwenden Sie eine beliebige Methode im Attribut (Unschärfe). Das ist alles.
quelle