मैं बाहर क्लिक पर ड्रॉपडाउन को कैसे बंद कर सकता हूं?


144

जब उपयोगकर्ता उस ड्रॉपडाउन के बाहर कहीं भी क्लिक करता है, तो मैं अपना लॉगिन मेनू ड्रॉपडाउन बंद करना चाहूंगा, और मैं ऐसा करना चाहूंगा कि Angular2 के साथ और Angular2 "दृष्टिकोण" के साथ ...

मैंने एक समाधान लागू किया है, लेकिन मैं वास्तव में इसके साथ आत्मविश्वास महसूस नहीं करता हूं। मुझे लगता है कि समान परिणाम प्राप्त करने का एक आसान तरीका होना चाहिए, इसलिए यदि आपके पास कोई विचार है ... तो चर्चा करें :)!

यहाँ मेरा कार्यान्वयन है:

ड्रॉपडाउन घटक:

यह मेरी ड्रॉपडाउन के लिए घटक है:

  • हर बार इस घटक यह दृश्यमान पर सेट, (उदाहरण के लिए: जब एक बटन पर उपयोगकर्ता क्लिक यह प्रदर्शित करने के लिए) यह एक "वैश्विक" rxjs विषय की सदस्यता userMenu में संग्रहीत SubjectsService
  • और हर बार जब यह छिपा होता है, तो यह इस विषय पर सदस्यता समाप्त कर देता है।
  • इस घटक के टेम्प्लेट के भीतर हर क्लिक कहीं भी ऑनकिक () विधि को ट्रिगर करता है , जो शीर्ष पर (और एप्लिकेशन घटक) ईवेंट बब्बलिंग को रोक देता है

यहाँ कोड है

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

आवेदन घटक:

दूसरी ओर, अनुप्रयोग घटक है (जो ड्रॉपडाउन घटक का जनक है):

  • यह घटक हर क्लिक इवेंट को पकड़ता है और एक ही rxjs विषय ( userMenu ) पर निकलता है

यहाँ कोड है:

export class AppComponent {

    constructor( public subjects: SubjectsService) {
        document.addEventListener('click', () => this.onClick());
    }
    onClick( ) {
        this.subjects.userMenu.next({});
    }
}

मुझे क्या परेशान:

  1. मैं उन घटकों के बीच संबंधक के रूप में कार्य करने वाले वैश्विक विषय के विचार के साथ वास्तव में सहज महसूस नहीं करता।
  2. SetTimeout : यह इसलिए आवश्यक है क्योंकि यहाँ क्या अन्यथा ऐसा है कि अगर लटकती दिखाने के बटन पर उपयोगकर्ता क्लिक है:
    • उपयोगकर्ता ड्रॉपडाउन दिखाने के लिए बटन (जो ड्रॉपडाउन घटक का हिस्सा नहीं है) पर क्लिक करता है।
    • ड्रॉपडाउन प्रदर्शित किया गया है और यह तुरंत उपयोगकर्तामेनू विषय की सदस्यता लेता है
    • क्लिक इवेंट बबल अप करने के लिए एप्लिकेशन घटक है और पकड़ा जाता है
    • अनुप्रयोग घटक userMenu विषय पर एक घटना का उत्सर्जन करता है
    • ड्रॉपडाउन घटक userMenu पर इस क्रिया को पकड़ता है और ड्रॉपडाउन को छिपाता है।
    • अंत में ड्रॉपडाउन कभी प्रदर्शित नहीं होता है।

इस सेटआउट ने सदस्यता को वर्तमान जावास्क्रिप्ट कोड टर्न के अंत में विलंबित कर दिया है जो समस्या को हल करता है, लेकिन मेरी राय में बहुत ही सुरुचिपूर्ण तरीके से।

यदि आप क्लीनर, बेहतर, होशियार, तेज या मजबूत समाधान जानते हैं, तो कृपया मुझे बताएं :)!


ये जवाब आपको कुछ विचार दे सकते हैं: stackoverflow.com/a/35028820/215945 , stackoverflow.com/questions/35024495#35024651
मार्क राजकोक

जवाबों:


245

आप (document:click)ईवेंट का उपयोग कर सकते हैं :

@Component({
  host: {
    '(document:click)': 'onClick($event)',
  },
})
class SomeComponent() {
  constructor(private _eref: ElementRef) { }

  onClick(event) {
   if (!this._eref.nativeElement.contains(event.target)) // or some similar check
     doSomething();
  }
}

एक अन्य दृष्टिकोण निर्देश के रूप में कस्टम इवेंट बनाना है। बेन नडेल द्वारा इन पदों की जाँच करें:


1
@ सास्का धन्यवाद, और सहमत मुझे लगा कि अगर कोई गैर-अपग्रेड किया गया एपीआई डॉक्टर है, तो यह उस खोज में दिखाई देगा जिसने मुझे यहां पहुंचाया।
danludwig

4
अगर Event.target एक ऐसा तत्व है, जिसे [आंतरिक HTML] बाइंडिंग की तरह गतिशील रूप से जोड़ा गया है, तो एलिमेंटरीफ की नेटिवमेंट में यह शामिल नहीं होगा।
पैट्रिक ग्राहम

8
इस तकनीक का एकमात्र नकारात्मक पहलू यह है कि अब आपके आवेदन में एक क्लिक ईवेंट श्रोता है जो आपके द्वारा क्लिक किए जाने पर हर बार फायर करता है।
कोडेपिक

37
आधिकारिक एंगुलर 2 स्टाइल गाइड के अनुसार, आपको डेकोरेटर पर संपत्ति के @HostListener('document:click', ['$event'])बजाय उपयोग करना चाहिए । hostComponent
मिचेल मिस्स्किस्ज़िन

15
या आप इसके लिए बस rxjs का उपयोग कर सकते हैं, जैसे Observable.fromEvent(document, 'click').subscribe(event => {your code here}), इसलिए आप हमेशा केवल तभी सदस्यता ले सकते हैं जब आपको उदाहरण के लिए सुनने की आवश्यकता होती है जिसे आपने ड्रॉपडाउन खोला था, और जब आप इसे बंद करते हैं तो आप इसे बंद कर देते हैं
ब्लाइंड

43

प्रभावी विधि

मुझे यह clickOutनिर्देश मिला : https://github.com/chliebel/angular2-click-outside । मैं इसकी जांच करता हूं और यह अच्छी तरह से काम करता है (मैं केवल clickOutside.directive.tsअपने प्रोजेक्ट की नकल करता हूं)। यू इसे इस तरह से उपयोग कर सकते हैं:

<div (clickOutside)="close($event)"></div>

closeआपका फ़ंक्शन कहां है जो उपयोगकर्ता द्वारा div के बाहर क्लिक करने पर कॉल किया जाएगा। प्रश्न में वर्णित समस्या को संभालने के लिए यह बहुत ही सुंदर तरीका है।

यदि आप पॉपअप विंडो को बंद करने के लिए ऊपर दिए गए निर्देश का उपयोग करते हैं, तो सबसे पहले याद रखें event.stopPropagation()कि बटन क्लिक इवेंट हैंडलर जो पॉपअप ओपन करता है।

बक्शीश:

नीचे मैं फ़ाइल से oryginal निर्देश कोड की प्रतिलिपि बनाता हूं clickOutside.directive.ts(यदि भविष्य में लिंक काम करना बंद कर देगा) - लेखक ईसाई लाईबेल है :


2
@Vega मेरा पुनर्मूल्यांकन * एनजीआईएफ के साथ एक तत्व में निर्देश का उपयोग करने के लिए होगा, ड्रॉपडाउन के मामले में, यह कुछ इस तरह हो सकता है<div class="wrap" *ngIf="isOpened" (clickOutside)="...// this should set this.isOpen=false"
गेब्रियल बाल्सा कैंट

19

मैंने इसे इस तरह किया है।

एक घटना श्रोता को दस्तावेज़ पर जोड़ा clickऔर उस हैंडलर में जाँच की कि क्या मेरा containerहै event.target, यदि नहीं - ड्रॉपडाउन छिपाएँ।

यह इस तरह दिखेगा।

@Component({})
class SomeComponent {
    @ViewChild('container') container;
    @ViewChild('dropdown') dropdown;

    constructor() {
        document.addEventListener('click', this.offClickHandler.bind(this)); // bind on doc
    }

    offClickHandler(event:any) {
        if (!this.container.nativeElement.contains(event.target)) { // check click origin
            this.dropdown.nativeElement.style.display = "none";
        }
    }
}

नमस्ते। क्या The.bind (यह) आवश्यक है?
द्रेनाई जूल

1
@ ब्रायन यह आवश्यक हो सकता है या नहीं भी हो सकता है, लेकिन निश्चित रूप से ऐसा नहीं होगा यदि वह this.offClickHandlerएक एरो फंक्शन में लिपटा हो ।
लांसाना कैमारा

17

मुझे लगता है कि सक्साका ज्यादातर लोगों के लिए जवाब काम करता है। हालांकि, मेरे पास एक स्थिति थी, जहां तत्व की सामग्री, जिसे ऑफ-क्लिक घटनाओं को सुनना चाहिए, गतिशील रूप से बदल गया। इसलिए तत्व नेटिवमेंट में Event.target शामिल नहीं था, जब इसे गतिशील रूप से बनाया गया था। मैं इसे निम्नलिखित निर्देश के साथ हल कर सकता हूं

@Directive({
  selector: '[myOffClick]'
})
export class MyOffClickDirective {

  @Output() offClick = new EventEmitter();

  constructor(private _elementRef: ElementRef) {
  }

  @HostListener('document:click', ['$event.path'])
  public onGlobalClick(targetElementPath: Array<any>) {
    let elementRefInPath = targetElementPath.find(e => e === this._elementRef.nativeElement);
    if (!elementRefInPath) {
      this.offClick.emit(null);
    }
  }
}

इसकी बजाय कि अगर elementRef Event.target में जाँच करता है, तो मैं जाँचता हूँ कि क्या elementRef घटना के पथ (DOM पथ को लक्षित करने के लिए) में है। इस तरह से गतिशील रूप से बनाए गए तत्वों को संभालना संभव है।


धन्यवाद - यह बेहतर काम करता है जब बाल घटक मौजूद होते हैं
MAHsan

यह मेरे लिए काफी मददगार था। सुनिश्चित नहीं हैं कि घटक क्लिक के बाहर अन्य उत्तरों के साथ क्यों नहीं पता लगाया जा रहा है।
जावाक्वेस्ट

13

यदि आप iOS पर ऐसा कर रहे हैं, तो उपयोग करें touchstart ईवेंट का भी :

कोणीय 4 के रूप में, HostListenerसजावट ऐसा करने का पसंदीदा तरीका है

import { Component, OnInit, HostListener, ElementRef } from '@angular/core';
...
@Component({...})
export class MyComponent implement OnInit {

  constructor(private eRef: ElementRef){}

  @HostListener('document:click', ['$event'])
  @HostListener('document:touchstart', ['$event'])
  handleOutsideClick(event) {
    // Some kind of logic to exclude clicks in Component.
    // This example is borrowed Kamil's answer
    if (!this.eRef.nativeElement.contains(event.target) {
      doSomethingCool();
    }
  }

}

10

हम आज काम पर एक समान मुद्दे पर काम कर रहे हैं, यह पता लगाने की कोशिश कर रहे हैं कि ड्रॉपडाउन डिव को गायब कैसे किया जाता है जब इसे बंद किया जाता है। हमारा पोस्टर शुरुआती पोस्टर के सवाल से थोड़ा अलग है क्योंकि हम एक अलग घटक या निर्देश से दूर क्लिक नहीं करना चाहते थे , लेकिन केवल विशेष div के बाहर।

हमने (विंडो: माउसअप) ईवेंट हैंडलर का उपयोग करके इसे समाप्त कर दिया।

चरण:
1.) हमने पूरे ड्रॉपडाउन मेनू को एक अद्वितीय वर्ग नाम दिया।

2.) आंतरिक ड्रॉपडाउन मेनू में (केवल वह भाग जिसे हम मेनू बंद नहीं करने के लिए क्लिक करना चाहते थे), हमने एक (विंडो: माउसअप) ईवेंट हैंडलर जोड़ा और $ घटना में पारित कर दिया।

नोट: यह एक विशिष्ट "क्लिक" हैंडलर के साथ नहीं किया जा सकता है क्योंकि यह मूल क्लिक हैंडलर के साथ विवादित है।

3.) हमारे कंट्रोलर में, हमने वह विधि बनाई, जिसे हम क्लिक आउट ईवेंट पर कॉल करना चाहते थे, और हम यह पता लगाने के लिए ईवेंट का उपयोग करते हैं। क्लोकेस्ट ( यहां डॉक्स ) यह पता लगाने के लिए कि क्लिक की गई जगह हमारे लक्षित-वर्ग div के भीतर है या नहीं।

 autoCloseForDropdownCars(event) {
        var target = event.target;
        if (!target.closest(".DropdownCars")) { 
            // do whatever you want here
        }
    }
 <div class="DropdownCars">
   <span (click)="toggleDropdown(dropdownTypes.Cars)" class="searchBarPlaceholder">Cars</span>
   <div class="criteriaDropdown" (window:mouseup)="autoCloseForDropdownCars($event)" *ngIf="isDropdownShown(dropdownTypes.Cars)">
   </div>
</div>


"विंडो: माउसअप" का उपयोग होस्ट डेकोरेटर के भीतर किया जाना चाहिए।
शिवम

@ शिवम-- मुझे यकीन नहीं है कि आपके द्वारा "होस्ट डेकोरेटर के भीतर" का उपयोग किया जाना चाहिए। क्या आप संभवतः आगे बता सकते हैं? धन्यवाद!
पैगी बोल्ड्यूक

मेरा मतलब है कि सीधे "विंडो" ऑब्जेक्ट का उपयोग करने के बजाय, आपको घटक डेकोरेटर की "होस्ट" संपत्ति का उपयोग करना चाहिए / घटक के "HostListener" डेकोरेटर। कोणीय 2 में "विंडो" या "डॉक्यूमेंट" ऑब्जेक्ट के साथ काम करते समय यह मानक अभ्यास है।
शिवम

2
बस ब्राउज़र अनुकूलता के लिए नज़र रखें, .closest()आईई / एज पर समर्थित नहीं है क्योंकि आज ( कैनीयूज़ )
सुपरजोस

5

आप ड्रॉपडाउन में एक सिबलिंग तत्व बना सकते हैं जो पूरी स्क्रीन को कवर करता है जो कि अदृश्य होगा और वहां क्लिक इवेंट कैप्चर करने के लिए होगा। तब आप उस तत्व पर क्लिक का पता लगा सकते हैं और क्लिक होने पर ड्रॉपडाउन को बंद कर सकते हैं। बता दें कि एलिमेंट क्लास सिल्क्सस्क्रीन का है, यहां इसके लिए कुछ स्टाइल है:

.silkscreen {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1;
}

Z- इंडेक्स को हर चीज से ऊपर रखने के लिए पर्याप्त उच्च होना चाहिए, लेकिन आपका ड्रॉपडाउन। इस स्थिति में मेरा ड्रॉपडाउन z-index 2 होगा।

अन्य उत्तरों ने मेरे लिए कुछ मामलों में काम किया, सिवाय इसके कि कभी-कभी मेरा ड्रॉपडाउन बंद हो जाता है जब मैंने इसके भीतर तत्वों के साथ बातचीत की और मैं ऐसा नहीं चाहता था। मेरे पास गतिशील रूप से जोड़े गए तत्व थे, जो मेरे लक्ष्य के अनुसार, मेरे लक्ष्य की तरह, मेरे घटक में समाहित नहीं थे। उस मेस को छांटने के बजाय मुझे लगा कि मैं इसे सिक्सस्क्रीन तरीके से आज़माऊँगा।


5

मैंने कोई वर्कअराउंड नहीं किया। मैंने अभी-अभी दस्तावेज़ संलग्न किया है: मेरे टॉगल फ़ंक्शन पर निम्नानुसार क्लिक करें:

    @Directive ({
      चयनकर्ता: '[appDropDown]'
    })
    निर्यात वर्ग ड्रॉपडाउन अप्रत्यक्ष रूप से लागू होता है {

      @HostBinding ('class.open') isOpen: बूलियन;

      कंस्ट्रक्टर (निजी एलएम रीफ: एलिमेंटरीफ) {}

      ngOnInit (): शून्य {
        this.isOpen = false;
      }

      @HostListener ('दस्तावेज़: क्लिक करें', ['$ घटना'])
      @HostListener ('दस्तावेज़: टचस्टार्ट', ['$ घटना']]
      टॉगल (घटना) {
        अगर (it.elemRef.nativeElement.contains (event.target)) {
          this.isOpen =! this.isOpen;
        } अन्य {
          this.isOpen = false;
      }
    }

इसलिए, जब मैं अपने निर्देश के बाहर हूं, मैं ड्रॉपडाउन को बंद कर देता हूं।


4
import { Component, HostListener } from '@angular/core';

@Component({
    selector: 'custom-dropdown',
    template: `
        <div class="custom-dropdown-container">
            Dropdown code here
        </div>
    `
})
export class CustomDropdownComponent {
    thisElementClicked: boolean = false;

    constructor() { }

    @HostListener('click', ['$event'])
    onLocalClick(event: Event) {
        this.thisElementClicked = true;
    }

    @HostListener('document:click', ['$event'])
    onClick(event: Event) {
        if (!this.thisElementClicked) {
            //click was outside the element, do stuff
        }
        this.thisElementClicked = false;
    }
}

DOWNSIDES: - पेज पर इन घटकों में से प्रत्येक के लिए दो क्लिक इवेंट श्रोता। इसका उपयोग उन घटकों पर न करें जो पृष्ठ के सैकड़ों बार हैं।


नहीं, मैंने केवल डेस्कटॉप ब्राउज़र पर इसका उपयोग किया है।
एलेक्स एगली

3

मैं @ उत्तर का पूरक करना चाहूंगा, क्योंकि घटक के बाहर क्लिक के बाद घटना को हटाया नहीं जा रहा है। पूर्ण रसीद:

  • #Container के साथ अपने मुख्य तत्व को चिह्नित करें

    @ViewChild('container') container;
    
    _dropstatus: boolean = false;
    get dropstatus() { return this._dropstatus; }
    set dropstatus(b: boolean) 
    {
        if (b) { document.addEventListener('click', this.offclickevent);}
        else { document.removeEventListener('click', this.offclickevent);}
        this._dropstatus = b;
    }
    offclickevent: any = ((evt:any) => { if (!this.container.nativeElement.contains(evt.target)) this.dropstatus= false; }).bind(this);
  • क्लिक करने योग्य तत्व पर, उपयोग करें:

    (click)="dropstatus=true"

अब आप ड्रॉपस्टैट चर के साथ अपनी ड्रॉपडाउन स्थिति को नियंत्रित कर सकते हैं, और [ngClass] के साथ उचित कक्षाएं लागू कर सकते हैं ...


3

आप निर्देश लिख सकते हैं:

@Directive({
  selector: '[clickOut]'
})
export class ClickOutDirective implements AfterViewInit {
  @Input() clickOut: boolean;

  @Output() clickOutEvent: EventEmitter<any> = new EventEmitter<any>();

  @HostListener('document:mousedown', ['$event']) onMouseDown(event: MouseEvent) {

       if (this.clickOut && 
         !event.path.includes(this._element.nativeElement))
       {
           this.clickOutEvent.emit();
       }
  } 


}

आपके घटक में:

@Component({
  selector: 'app-root',
  template: `
    <h1 *ngIf="isVisible" 
      [clickOut]="true" 
      (clickOutEvent)="onToggle()"
    >{{title}}</h1>
`,
  styleUrls: ['./app.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
  title = 'app works!';

  isVisible = false;

  onToggle() {
    this.isVisible = !this.isVisible;
  }
}

जब HTML तत्व DOM में हो रहा हो और जब [clickOut] इनपुट प्रॉपर्टी 'सही' हो, तब यह निर्देश उत्सर्जित करता है। यह ईवेंट से तत्व को हटाने से पहले ईवेंट को संभालने के लिए मूसडाउन ईवेंट सुनता है।

और एक नोट: फ़ायरफ़ॉक्स में संपत्ति 'पथ' शामिल नहीं है घटना पर आप पथ बनाने के लिए फ़ंक्शन का उपयोग कर सकते हैं:

const getEventPath = (event: Event): HTMLElement[] => {
  if (event['path']) {
    return event['path'];
  }
  if (event['composedPath']) {
    return event['composedPath']();
  }
  const path = [];
  let node = <HTMLElement>event.target;
  do {
    path.push(node);
  } while (node = node.parentElement);
  return path;
};

इसलिए आपको निर्देशन पर ईवेंट हैंडलर को बदलना चाहिए: ईवेंट.पथ को getEventPath (ईवेंट) को बदला जाना चाहिए

यह मॉड्यूल मदद कर सकता है। https://www.npmjs.com/package/ngx-clickout इसमें एक ही तर्क है, लेकिन स्रोत HTML तत्व पर esc ईवेंट को भी संभालना है।


3

सही उत्तर में एक समस्या है, यदि आपके पास अपनी आबादी में एक क्लैसिकल घटक है, तो तत्व अब containविधि पर नहीं होगा और बंद हो जाएगा, @ JuHarm89 के आधार पर मैंने अपना स्वयं का बनाया:

export class PopOverComponent implements AfterViewInit {
 private parentNode: any;

  constructor(
    private _element: ElementRef
  ) { }

  ngAfterViewInit(): void {
    this.parentNode = this._element.nativeElement.parentNode;
  }

  @HostListener('document:click', ['$event.path'])
  onClickOutside($event: Array<any>) {
    const elementRefInPath = $event.find(node => node === this.parentNode);
    if (!elementRefInPath) {
      this.closeEventEmmit.emit();
    }
  }
}

सहायता के लिए धन्यवाद!


2

@ महान समाधान के लिए एक बेहतर संस्करण:

@Component({})
class SomeComponent {
    @ViewChild('container') container;
    @ViewChild('dropdown') dropdown;

    constructor() {
        document.addEventListener('click', this.offClickHandler.bind(this)); // bind on doc
    }

    offClickHandler(event:any) {
        if (!this.container.nativeElement.contains(event.target)) { // check click origin

            this.dropdown.nativeElement.closest(".ourDropdown.open").classList.remove("open");

        }
    }
}

यदि आप बूटस्ट्रैप ड्रॉप-डाउन का उपयोग करते हैं, तो css फ़ाइल में: // आवश्यक नहीं है।

.ourDropdown{
   display: none;
}
.ourDropdown.open{
   display: inherit;
}

2

यदि आप इसके बजाय मोडल ओवरले पर क्लिक करते हैं, तो आपको बहुत आसान होना चाहिए।

आपका टेम्प्लेट:

<div #modalOverlay (click)="clickOutside($event)" class="modal fade show" role="dialog" style="display: block;">
        <div class="modal-dialog" [ngClass]='size' role="document">
            <div class="modal-content" id="modal-content">
                <div class="close-modal" (click)="closeModal()"> <i class="fa fa-times" aria-hidden="true"></i></div>
                <ng-content></ng-content>
            </div>
        </div>
    </div>

और विधि:

  @ViewChild('modalOverlay') modalOverlay: ElementRef;

// ... your constructor and other method

      clickOutside(event: Event) {
    const target = event.target || event.srcElement;
    console.log('click', target);
    console.log("outside???", this.modalOverlay.nativeElement == event.target)
    // const isClickOutside = !this.modalBody.nativeElement.contains(event.target);
    // console.log("click outside ?", isClickOutside);
    if ("isClickOutside") {
      // this.closeModal();
    }


  }

2

मैंने इसी तरह की समस्या को दूर करने का निर्देश दिया है और मैं बूटस्ट्रैप का उपयोग कर रहा हूं। लेकिन मेरे मामले में, मौजूदा खुले ड्रॉपडाउन मेनू को बंद करने के लिए तत्व के बाहर की क्लिक घटना की प्रतीक्षा करने के बजाय मुझे लगता है कि यह बेहतर है अगर हम मेनू को स्वचालित रूप से बंद करने के लिए 'मूसलीव' घटना पर देखते हैं।

यहाँ मेरा समाधान है:

आदेश

import { Directive, HostListener, HostBinding } from '@angular/core';
@Directive({
  selector: '[appDropdown]'
})
export class DropdownDirective {

  @HostBinding('class.open') isOpen = false;

  @HostListener('click') toggleOpen() {
    this.isOpen = !this.isOpen;
  }

  @HostListener('mouseleave') closeDropdown() {
    this.isOpen = false;
  }

}

एचटीएमएल

<ul class="nav navbar-nav navbar-right">
    <li class="dropdown" appDropdown>
      <a class="dropdown-toggle" data-toggle="dropdown">Test <span class="caret"></span>
      </a>
      <ul class="dropdown-menu">
          <li routerLinkActive="active"><a routerLink="/test1">Test1</a></li>
          <li routerLinkActive="active"><a routerLink="/test2/">Test2</a></li>
      </ul>
    </li>
</ul>

1

यदि आप बूटस्ट्रैप का उपयोग कर रहे हैं, तो आप इसे सीधे ड्रापडाउन (बूटस्ट्रैप घटक) के माध्यम से कर सकते हैं।

<div class="input-group">
    <div class="input-group-btn">
        <button aria-expanded="false" aria-haspopup="true" class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">
            Toggle Drop Down. <span class="fa fa-sort-alpha-asc"></span>
        </button>
        <ul class="dropdown-menu">
            <li>List 1</li>
            <li>List 2</li>
            <li>List 3</li>
        </ul>
    </div>
</div>

अब (click)="clickButton()"बटन पर सामान रखना ठीक है । http://getbootstrap.com/javascript/#dropdowns


1

मैंने खुद का थोड़ा सा काम भी किया।

मैंने एक (dropdownOpen) ईवेंट बनाया, जिसे मैं अपने एनजी-सिलेक्ट एलिमेंट कंपोनेंट में सुनता हूं और एक फंक्शन को कॉल करता हूं, जो वर्तमान में खोले गए सिलेक्ट कॉमपॉन्टेंट के अलावा अन्य सभी SelectComponent की ओपनिंग को बंद कर देगा।

मैंने इवेंट को खाली करने के लिए select.ts फ़ाइल के अंदर एक फ़ंक्शन को संशोधित किया :

private open():void {
    this.options = this.itemObjects
        .filter((option:SelectItem) => (this.multiple === false ||
        this.multiple === true && !this.active.find((o:SelectItem) => option.text === o.text)));

    if (this.options.length > 0) {
        this.behavior.first();
    }
    this.optionsOpened = true;
    this.dropdownOpened.emit(true);
}

HTML में मैंने एक ईवेंट श्रोता को (dropdownOpened) जोड़ा :

<ng-select #elem (dropdownOpened)="closeOtherElems(elem)"
    [multiple]="true"
    [items]="items"
    [disabled]="disabled"
    [isInputAllowed]="true"
    (data)="refreshValue($event)"
    (selected)="selected($event)"
    (removed)="removed($event)"
    placeholder="No city selected"></ng-select>

यह एनजी 2-चयन टैग वाले घटक के अंदर इवेंट ट्रिगर पर मेरा कॉलिंग फ़ंक्शन है:

@ViewChildren(SelectComponent) selectElem :QueryList<SelectComponent>;

public closeOtherElems(element){
    let a = this.selectElem.filter(function(el){
                return (el != element)
            });

    a.forEach(function(e:SelectComponent){
        e.closeDropdown();
    })
}

1

ध्यान दें: वेब कर्मचारियों का उपयोग करने के इच्छुक लोगों के लिए और आपको दस्तावेज़ और नेटिवमेंट के उपयोग से बचने की आवश्यकता है।

मैंने यहां एक ही सवाल का जवाब दिया: /programming/47571144

उपरोक्त लिंक से कॉपी / पेस्ट करें:

मेरे पास एक ही मुद्दा था जब मैं ड्रॉप-डाउन मेनू बना रहा था और एक पुष्टिकरण संवाद मैं बाहर क्लिक करने पर उन्हें खारिज करना चाहता था।

मेरा अंतिम कार्यान्वयन पूरी तरह से काम करता है लेकिन कुछ css3 एनिमेशन और स्टाइल की आवश्यकता होती है।

ध्यान दें : मैंने नीचे दिए गए कोड का परीक्षण नहीं किया है, कुछ सिंटैक्स समस्याएं हो सकती हैं, जिन्हें इस्त्री करने की आवश्यकता है, आपकी अपनी परियोजना के लिए स्पष्ट समायोजन भी!

मैंने क्या किया:

मैंने ऊंचाई 100%, चौड़ाई 100% और परिवर्तन के साथ एक अलग निश्चित डिव बनाया: स्केल (0), यह मूल रूप से पृष्ठभूमि है, आप इसे पृष्ठभूमि-रंग के साथ स्टाइल कर सकते हैं: आरजीबीए (0, 0, 0, 0.466); स्पष्ट करने के लिए मेनू खुला है और पृष्ठभूमि क्लिक-टू-क्लोज़ है। मेनू को हर चीज़ के मुकाबले एक z-index मिलता है, फिर बैकग्राउंड div को मेनू की तुलना में z-index कम मिलता है, लेकिन बाकी सभी चीज़ों की तुलना में अधिक। फिर बैकग्राउंड में एक क्लिक इवेंट होता है जो ड्रॉप-डाउन को बंद करता है।

यहाँ यह आपके html कोड के साथ है।

<div class="dropdownbackground" [ngClass]="{showbackground: qtydropdownOpened}" (click)="qtydropdownOpened = !qtydropdownOpened"><div>
<div class="zindex" [class.open]="qtydropdownOpened">
  <button (click)="qtydropdownOpened = !qtydropdownOpened" type="button" 
         data-toggle="dropdown" aria-haspopup="true" [attr.aria-expanded]="qtydropdownOpened ? 'true': 'false' ">
   {{selectedqty}}<span class="caret margin-left-1x "></span>
 </button>
  <div class="dropdown-wrp dropdown-menu">
  <ul class="default-dropdown">
      <li *ngFor="let quantity of quantities">
       <a (click)="qtydropdownOpened = !qtydropdownOpened;setQuantity(quantity)">{{quantity  }}</a>
       </li>
   </ul>
  </div>
 </div>

यहाँ css3 है जिसे कुछ सरल एनिमेशन की आवश्यकता है।

/* make sure the menu/drop-down is in front of the background */
.zindex{
    z-index: 3;
}

/* make background fill the whole page but sit behind the drop-down, then
scale it to 0 so its essentially gone from the page */
.dropdownbackground{
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: 2;
    transform: scale(0);
    opacity: 0;
    background-color: rgba(0, 0, 0, 0.466);
}

/* this is the class we add in the template when the drop down is opened
it has the animation rules set these how you like */
.showbackground{
    animation: showBackGround 0.4s 1 forwards; 

}

/* this animates the background to fill the page
if you don't want any thing visual you could use a transition instead */
@keyframes showBackGround {
    1%{
        transform: scale(1);
        opacity: 0;
    }
    100% {
        transform: scale(1);
        opacity: 1;
    }
}

यदि आप कुछ भी दृश्य के बाद नहीं हैं, तो आप इस तरह एक संक्रमण का उपयोग कर सकते हैं

.dropdownbackground{
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: 2;
    transform: scale(0);
    opacity: 0;
    transition all 0.1s;
}

.dropdownbackground.showbackground{
     transform: scale(1);
}

1

मैं एक अन्य समाधान पर आया था, जो फ़ोकस / ब्लर घटना के उदाहरणों से प्रेरित था।

इसलिए, यदि आप वैश्विक दस्तावेज़ श्रोता को संलग्न किए बिना समान कार्यक्षमता प्राप्त करना चाहते हैं, तो आप निम्न उदाहरण को मान्य मान सकते हैं। यह OSx पर सफारी और फ़ायरफ़ॉक्स में भी काम करता है, जबकि उनके पास बटन फोकस इवेंट की अन्य हैंडलिंग है: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus

कोणीय 8 के साथ स्टैकबिज पर काम करने का उदाहरण: https://stackblitz.com/edit/angular-sv4tbi?file=src%2Ftoggle-dropdown%2Ftoggle-dropdown.directive.ts

HTML मार्कअप:

<div class="dropdown">
  <button class="btn btn-secondary dropdown-toggle" type="button" aria-haspopup="true" aria-expanded="false">Dropdown button</button>
  <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
    <a class="dropdown-item" href="#">Action</a>
    <a class="dropdown-item" href="#">Another action</a>
    <a class="dropdown-item" href="#">Something else here</a>
  </div>
</div>

निर्देश इस तरह दिखेगा:

import { Directive, HostBinding, ElementRef, OnDestroy, Renderer2 } from '@angular/core';

@Directive({
  selector: '.dropdown'
})
export class ToggleDropdownDirective {

  @HostBinding('class.show')
  public isOpen: boolean;

  private buttonMousedown: () => void;
  private buttonBlur: () => void;
  private navMousedown: () => void;
  private navClick: () => void;

  constructor(private element: ElementRef, private renderer: Renderer2) { }

  ngAfterViewInit() {
    const el = this.element.nativeElement;
    const btnElem = el.querySelector('.dropdown-toggle');
    const menuElem = el.querySelector('.dropdown-menu');

    this.buttonMousedown = this.renderer.listen(btnElem, 'mousedown', (evt) => {
      console.log('MOUSEDOWN BTN');
      this.isOpen = !this.isOpen;
      evt.preventDefault(); // prevents loose of focus (default behaviour) on some browsers
    });

    this.buttonMousedown = this.renderer.listen(btnElem, 'click', () => {
      console.log('CLICK BTN');
      // firefox OSx, Safari, Ie OSx, Mobile browsers.
      // Whether clicking on a <button> causes it to become focused varies by browser and OS.
      btnElem.focus();
    });

    // only for debug
    this.buttonMousedown = this.renderer.listen(btnElem, 'focus', () => {
      console.log('FOCUS BTN');
    });

    this.buttonBlur = this.renderer.listen(btnElem, 'blur', () => {
      console.log('BLUR BTN');
      this.isOpen = false;
    });

    this.navMousedown = this.renderer.listen(menuElem, 'mousedown', (evt) => {
      console.log('MOUSEDOWN MENU');
      evt.preventDefault(); // prevents nav element to get focus and button blur event to fire too early
    });
    this.navClick = this.renderer.listen(menuElem, 'click', () => {
      console.log('CLICK MENU');
      this.isOpen = false;
      btnElem.blur();
    });
  }

  ngOnDestroy() {
    this.buttonMousedown();
    this.buttonBlur();
    this.navMousedown();
    this.navClick();
  }
}

1

आप उपयोग कर सकते हैं mouseleave इस तरह से अपने विचार में

कोणीय 8 के साथ परीक्षण करें और पूरी तरह से काम करें

<ul (mouseleave)="closeDropdown()"> </ul>

यह कंटेनर को बंद कर देगा जब माउस निकल जाएगा, लेकिन वैसे भी साझा करने के लिए धन्यवाद, क्योंकि मैं इसके अस्तित्व से अनजान था।
बेन हेवर्ड

0

सबसे बड़ा विधि: डी

ऐसा करने का एक आसान तरीका है, इसके लिए किसी निर्देश की आवश्यकता नहीं है।

"एलिमेंट-दैट-टॉगल-योर-ड्रॉपडाउन" बटन टैग होना चाहिए। (धुंधला) विशेषता में किसी भी विधि का उपयोग करें। बस इतना ही।

<button class="element-that-toggle-your-dropdown"
               (blur)="isDropdownOpen = false"
               (click)="isDropdownOpen = !isDropdownOpen">
</button>

यदि आप ड्रॉपडाउन को खुले में रखना चाहते हैं, तो यह काम नहीं करेगा, उदाहरण के लिए, उपयोगकर्ता एक बटन पर क्लिक करने से चूक सकता है
Yeswhen
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.