কৌণিক উপাদানটির বাইরে ক্লিক করুন


উত্তর:


196
import { Component, ElementRef, HostListener, Input } from '@angular/core';

@Component({
  selector: 'selector',
  template: `
    <div>
      {{text}}
    </div>
  `
})
export class AnotherComponent {
  public text: String;

  @HostListener('document:click', ['$event'])
  clickout(event) {
    if(this.eRef.nativeElement.contains(event.target)) {
      this.text = "clicked inside";
    } else {
      this.text = "clicked outside";
    }
  }

  constructor(private eRef: ElementRef) {
    this.text = 'no clicks yet';
  }
}

একটি কাজের উদাহরণ - এখানে ক্লিক করুন


14
ট্রিগার উপাদানটির অভ্যন্তরে এনজিআইফ দ্বারা নিয়ন্ত্রিত কোনও উপাদান থাকা সত্ত্বেও এটি কাজ করে না, যেহেতু এনজিআইফটি ডিওএম থেকে উপাদানটি অপসারণের ক্লিক ইভেন্টের আগে ঘটে: plnkr.co/edit/spctsLxkFCxNqLtfzE5q?p=preview
জে ফ্রাঙ্কেনস্টাইন

এটি কি এমন কোনও উপাদানটিতে কাজ করে যা গতিশীলভাবে এর মাধ্যমে তৈরি করেছে: কনট ফ্যাক্টরি = this.resolver.resolveComp घटकFactory (মাই কম্পোম্পোন্ট); কনস্ট ইলেম = this.vcr.createComp घटक (কারখানা);
আভি মোরালি

4
এই বিষয়টির একটি চমৎকার নিবন্ধ: খ্রিস্টিয়ালিয়বেল.কম
মিগুয়েল লারা

51

আমাগিয়রের উত্তরের বিকল্প। আপনি যখন কোনও এনজিআইএফ দিয়ে ডোম থেকে সরিয়ে ফেলা উপাদানটিতে ক্লিক করেন তখন এই সংস্করণটি কাজ করে।

http://plnkr.co/edit/4mrn4GjM95uvSbQtxrAS?p= পূর্বরূপ

  private wasInside = false;
  
  @HostListener('click')
  clickInside() {
    this.text = "clicked inside";
    this.wasInside = true;
  }
  
  @HostListener('document:click')
  clickout() {
    if (!this.wasInside) {
      this.text = "clicked outside";
    }
    this.wasInside = false;
  }


4
এটি এনজিআইএফ বা গতিশীল আপডেটগুলির সাথেও পুরোপুরি কাজ করে
বিকাশ কান্ডারী

4
এটি দুর্দান্ত
ভ্লাদিমির ডেমিরিভ

32

@ হোস্টলিস্টারের মাধ্যমে দস্তাবেজ ক্লিক করতে বাধ্য হওয়া ব্যয়বহুল। যদি আপনি অতিরিক্ত ব্যবহার করেন (উদাহরণস্বরূপ, একটি কাস্টম ড্রপডাউন উপাদান তৈরি করার সময় এবং আপনি কোনও ফর্মে একাধিক উদাহরণ তৈরি করেছেন) এটির দৃশ্যমান পারফরম্যান্সের প্রভাব থাকতে পারে।

আমি কেবলমাত্র আপনার মূল অ্যাপ্লিকেশনের অংশের মধ্যে ডকুমেন্ট ক্লিক ইভেন্টে একটি @ হোস্টলিস্টার () যুক্ত করার পরামর্শ দিচ্ছি। ইভেন্টটি বিশ্বব্যাপী ইউটিলিটি পরিষেবাতে সঞ্চিত কোনও জনসাধারণের ভিতরে ক্লিক করা লক্ষ্য উপাদানটির মানকে চাপ দিতে হবে।

@Component({
  selector: 'app-root',
  template: '<router-outlet></router-outlet>'
})
export class AppComponent {

  constructor(private utilitiesService: UtilitiesService) {}

  @HostListener('document:click', ['$event'])
  documentClick(event: any): void {
    this.utilitiesService.documentClickedTarget.next(event.target)
  }
}

@Injectable({ providedIn: 'root' })
export class UtilitiesService {
   documentClickedTarget: Subject<HTMLElement> = new Subject<HTMLElement>()
}

যে কেউ ক্লিক করা লক্ষ্য উপাদানটির জন্য আগ্রহী সে অবশ্যই আমাদের ইউটিলিটি পরিষেবাগুলির সর্বজনীন সাবস্ক্রাইবটি সাবস্ক্রাইব করতে হবে এবং উপাদানটি নষ্ট হয়ে গেলে সাবস্ক্রাইব করতে হবে।

export class AnotherComponent implements OnInit {

  @ViewChild('somePopup', { read: ElementRef, static: false }) somePopup: ElementRef

  constructor(private utilitiesService: UtilitiesService) { }

  ngOnInit() {
      this.utilitiesService.documentClickedTarget
           .subscribe(target => this.documentClickListener(target))
  }

  documentClickListener(target: any): void {
     if (this.somePopup.nativeElement.contains(target))
        // Clicked inside  
     else
        // Clicked outside
  }

5
আমি মনে করি যে এটি একটি গ্রহণযোগ্য উত্তর হয়ে উঠবে কারণ এটি অনেকগুলি অপ্টিমাইজেশনের জন্য অনুমতি দেয়: উদাহরণস্বরূপ
এডোয়ার্ডো 849

এটি ইন্টারনেটে আমার কাছে পাওয়া সবচেয়ে সুন্দর সমাধান
অনুপ বাংলে

4
@lampshade সঠিক আমি এই সম্পর্কে কথা বললাম। উত্তরটি আবার পড়ুন। আমি আপনার স্টাইলের (সাবস্ক্রিপশন.এডিডি ()) র সদস্যতা বাতিলকরণটি ছেড়ে দিচ্ছি। সাবস্ক্রাইব করতে ভুলবেন না!
জিনালেক্স

@ginalx আমি আপনার সমাধানটি প্রয়োগ করেছি, এটি প্রত্যাশার মতো কাজ করে। যদিও আমি এটি ব্যবহার করার মতো কোনও ইস্যুতে ছড়িয়ে পড়েছি। এখানে প্রশ্নটি রয়েছে, দয়া করে একবার দেখুন
নিলেশ

6

উপরে উল্লিখিত উত্তরগুলি সঠিক তবে আপনি যদি প্রাসঙ্গিক উপাদান থেকে ফোকাস হারাতে পরে একটি ভারী প্রক্রিয়া করছেন তবে কি হবে। তার জন্য, আমি দুটি পতাকা সহ একটি সমাধান নিয়ে এসেছি যেখানে কেবলমাত্র প্রাসঙ্গিক উপাদান থেকে ফোকাস হারিয়ে ফেললে ফোকাস আউট ইভেন্ট প্রক্রিয়াটি কেবল তখনই ঘটবে।

isFocusInsideComponent = false;
isComponentClicked = false;

@HostListener('click')
clickInside() {
    this.isFocusInsideComponent = true;
    this.isComponentClicked = true;
}

@HostListener('document:click')
clickout() {
    if (!this.isFocusInsideComponent && this.isComponentClicked) {
        // do the heavy process

        this.isComponentClicked = false;
    }
    this.isFocusInsideComponent = false;
}

আশা করি এটা তোমাকে সাহায্য করবে। যদি কিছু মিস করে থাকে তবে আমাকে সংশোধন করুন।


4

জিনালেক্সের উত্তরটি ডিফল্ট এক ইমো হিসাবে সেট করা উচিত: এই পদ্ধতিটি অনেকগুলি অনুকূলিতকরণের জন্য অনুমতি দেয়।

সমস্যাটি

বলুন যে আমাদের আইটেমের একটি তালিকা আছে এবং প্রতিটি আইটেমের উপরে আমরা একটি মেনু অন্তর্ভুক্ত করতে চাই যা টগল করা দরকার। আমরা একটি বোতামে একটি টগল অন্তর্ভুক্ত করি যা clickনিজেই কোনও ইভেন্টের জন্য শোনায় (click)="toggle()", তবে ব্যবহারকারী যখনই এর বাইরে ক্লিক করে মেনু টগল করতে চাই। যদি আইটেমগুলির তালিকাটি বৃদ্ধি পায় এবং আমরা @HostListener('document:click')প্রতিটি মেনুতে একটি সংযুক্ত করি , তবে আইটেমটির মধ্যে থাকা প্রতিটি মেনু পুরো ডকুমেন্টটিতে ক্লিক করার জন্য মেনুটি টগল হওয়া সত্ত্বেও শুরু হবে। সুস্পষ্ট পারফরম্যান্স সমস্যাগুলি ছাড়াও এটি অপ্রয়োজনীয়।

উদাহরণস্বরূপ, যখনই পপআপ ক্লিকের মাধ্যমে টগল হয়ে যায় তখনই সাবস্ক্রাইব করতে পারেন এবং কেবল তখনই "বাইরের ক্লিকগুলি" শুনতে শুনতে শুরু করতে পারেন।


isActive: boolean = false;

// to prevent memory leaks and improve efficiency, the menu
// gets loaded only when the toggle gets clicked
private _toggleMenuSubject$: BehaviorSubject<boolean>;
private _toggleMenu$: Observable<boolean>;

private _toggleMenuSub: Subscription;
private _clickSub: Subscription = null;


constructor(
 ...
 private _utilitiesService: UtilitiesService,
 private _elementRef: ElementRef,
){
 ...
 this._toggleMenuSubject$ = new BehaviorSubject(false);
 this._toggleMenu$ = this._toggleMenuSubject$.asObservable();

}

ngOnInit() {
 this._toggleMenuSub = this._toggleMenu$.pipe(
      tap(isActive => {
        logger.debug('Label Menu is active', isActive)
        this.isActive = isActive;

        // subscribe to the click event only if the menu is Active
        // otherwise unsubscribe and save memory
        if(isActive === true){
          this._clickSub = this._utilitiesService.documentClickedTarget
           .subscribe(target => this._documentClickListener(target));
        }else if(isActive === false && this._clickSub !== null){
          this._clickSub.unsubscribe();
        }

      }),
      // other observable logic
      ...
      ).subscribe();
}

toggle() {
    this._toggleMenuSubject$.next(!this.isActive);
}

private _documentClickListener(targetElement: HTMLElement): void {
    const clickedInside = this._elementRef.nativeElement.contains(targetElement);
    if (!clickedInside) {
      this._toggleMenuSubject$.next(false);
    }    
 }

ngOnDestroy(){
 this._toggleMenuSub.unsubscribe();
}

এবং, এতে *.component.html:


<button (click)="toggle()">Toggle the menu</button>


4
আপনি যেভাবে ভাবছেন তার সাথে আমি একমত হওয়ার পরেও আমি কোনও tapঅপারেটরে সমস্ত যুক্তি না ভরাট করার পরামর্শ দেব । পরিবর্তে, ব্যবহার করুন skipWhile(() => !this.isActive), switchMap(() => this._utilitiesService.documentClickedTarget), filter((target) => !this._elementRef.nativeElement.contains(target)), tap(() => this._toggleMenuSubject$.next(false))। এইভাবে আপনি আরও বেশি পরিমাণে আরএক্সজে ব্যবহার করতে এবং কিছু সাবস্ক্রিপশন এড়িয়ে যান।
গিজারাহ


2

উন্নতি করা হয়েছে @ জে। ফ্র্যাঙ্কেনস্টাইন উত্তর

  
  @HostListener('click')
  clickInside($event) {
    this.text = "clicked inside";
    $event.stopPropagation();
  }
  
  @HostListener('document:click')
  clickOutside() {
      this.text = "clicked outside";
  }


-1

আপনি ইভেন্ট ফাংশনটিকে (ফোকাসআউট) বা (অস্পষ্ট) কল করতে পারেন তারপরে আপনি আপনার কোডটি রেখেছেন

<div tabindex=0 (blur)="outsideClick()">raw data </div>
 

 outsideClick() {
  alert('put your condition here');
   }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.