typescript - Bileşen dışında tıklama çalışmıyor | Angular


Kodu için bir Stackblitz yaptım (link: https://stackblitz.com/edit/angular-iah7up ). Bu, tıklama içinde ve tıklama dışında iki ng-content'i değiştiren özel bileşenim.

import {Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output} from '@angular/core';
import {fromEvent, Subject} from "rxjs";
import {untilDestroyed} from "ngx-take-until-destroy";
import {filter, switchMapTo, take} from "rxjs/operators";

@Component({
    selector: 'app-editable-inplace',
    templateUrl: './editable-inplace.component.html',
    styleUrls: ['./editable-inplace.component.css']
})
export class EditableInplaceComponent implements OnInit, OnDestroy {

    @Output() update = new EventEmitter();
    mode: 'view' | 'edit' = 'view';
    editMode = new Subject();
    editMode$ = this.editMode.asObservable();


    ngOnInit(): void {
        this.viewModeHandler();
        this.editModeHandler();
    }

    constructor(private host: ElementRef) {
    }

    // This method must be present, even if empty.
    ngOnDestroy() {
        // To protect you, we'll throw an error if it doesn't exist.
    }

    private viewModeHandler() {
        fromEvent(this.element, 'click').pipe(
            untilDestroyed(this)
        ).subscribe(() => {
            console.log('clicked inside')
            this.editMode.next(true);
            this.mode = 'edit';

        });
    }

    private editModeHandler() {
        const clickOutside$ = fromEvent(document, 'click').pipe(
            filter(({target}) => {
                console.log(this.mode)
                console.log('parent', this.element, 'child', target)

                const ans = this.element.contains(target) === false
                console.log('clickoutside', ans)
                return ans
            }),
            take(1)
        )

        this.editMode$.pipe(
            switchMapTo(clickOutside$),
            untilDestroyed(this)
        ).subscribe(event => {

            this.update.next();
            this.mode = 'view';
        });
    }

    get element() {
        return this.host.nativeElement;
    }

    toViewMode() {
        this.update.next();
        this.mode = 'view';
    }

}

Şablon :

<div *ngIf="mode==='view'" >
    <ng-content select="[view]"></ng-content>
</div>
<div *ngIf="mode==='edit'" >
    <ng-content select="[edit]"></ng-content>
</div>

Kullanım :

<app-editable-inplace >
    <div view>
       <h3>Click to edit [not working :-( ]</h3>

    </div>
    <div edit>

              <input placeholder="Click to edit"   >

    </div>
</app-editable-inplace>

Ama Görünüme tıkladığımda, clickOutside$hemen tetikleniyor (nedenini bilmiyorum) Ayrıca, this.element.contains(target) === false satırı çalışmıyor çünkü konsolda host'un tıklanan öğeyi içerdiğini görebiliyorum fakat her zaman dışarı tıkladığımı söylüyor (bunu da bilmiyorum)

Cevap #1

Tarayıcıda birçok olay kabarcıklanır (ve click olayı bunlardan biridir). Bu, olayın hedef elementten doğrudan yukarıya doğru gitmesi anlamına gelir. Ve eğer aynı isimde bir olayı yukarıda bir yerde kaydettiyseniz, hedef işleyicide ne olursa olsun bu olay tetiklenecektir.

 document  =====================================================>  outsideHandler
  body                                                          /\
    ....                                                        ||  
    <app-editable-inplace>                                   bubbling
      <div view>                                                /\
        <h3>Click to edit [not working :-( ]</h3> <===== target || viewClickHandler
      </div>
      ...
    </app-editable-inplace>

Yani, zaten tahmin ettiğiniz gibi bu sebepten clickOutside$ anında tetiklenir.

Bunu düzeltmek için kullanabileceğiniz farklı yollar vardır:

1) En basit yol, olayın daha fazla yayılmasını önlemek için event.stopPropagation() kullanmaktır.

private viewModeHandler() {
    fromEvent(this.element, 'click').pipe(
        untilDestroyed(this)
    ).subscribe((e: any) => {
        console.log('clicked inside');
        e.stopPropagation(); <==================== add this

2) Bu işleyicilerde kabarcıklanma olayı aynı olduğundan, üst işleyicide bunu işlemeyi önlemek için bazı bayraklar ayarlayabilirsiniz.

 private viewModeHandler() {
    fromEvent(this.element, 'click').pipe(
        untilDestroyed(this)
    ).subscribe((e: any) => {
        console.log('clicked inside');
        e.fromInside = true;


 ...
 const clickOutside$ = fromEvent(document, 'click').pipe(
  filter((e: any) => {
    return !e.fromInside && this.element.contains(e.target) === false
  }),

3) Tıklama olayını yakalama aşamasında yakalayın:

fromEvent(this.element, 'click', { capture: true }).pipe(

... 

const clickOutside$ = fromEvent(document, 'click', { capture: true })

Cevap #2

Tıklama olayınızı tüm belgeye bağlamış olmanızın nedeni budur.

Bunun nedeniyle dışarı tıklama sayacınız artar.

Yapmanız gereken tek şey tıklama kontrol olayını işaretlemek ve ardından yayılmayı engellemektir.

Etkinlikler hakkında daha fazla bilgi edinmek için lütfen bu makaleyi inceleyin.

https://medium.com/@vsvaibhav2016/event-bubbling-and-event-capturing-in-javascript-6ff38bec30e