Нажмите за пределами компонента не работает |угловатый - PullRequest
2 голосов
/ 02 июля 2019

Я сделал Stackblitz для своего кода (ссылка: https://stackblitz.com/edit/angular-iah7up).Это мой пользовательский компонент, который переключает два нг-контента в зависимости от клика и клика.

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

}

Шаблон:

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

Использование:

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

    </div>
    <div edit>

              <input placeholder="Click to edit"   >

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

Но когда я нажимаю на представление, clickOutside $ запускается мгновенно (я не знаю почему), а также, строка this.element.contains(target) === false не работает, потому что я вижу в консоли, что хост содержит элемент, на который нажали, хотя всегда говорят, чтоЯ щелкнул снаружи (я тоже не знаю почему)

enter image description here

Ответы [ 2 ]

0 голосов
/ 03 июля 2019

В браузере многие события всплывают (и событие click является одним из них).Это означает, что событие идет от целевого элемента прямо вверх.И если вы зарегистрировали событие с тем же именем где-то выше, то оно будет запущено, несмотря на то, что произошло в целевом обработчике.

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

Итак, как вы уже догадались по этой причине, clickOutside$ запускается мгновенно.

Существуют различные способы исправить это:

1) Самый простой способ - использовать event.stopPropagation(), чтобы событие не всплыло дальше.

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

2) Поскольку событие всплытия в этих обработчиках одинаковое, вы можете установить некоторый флаг, чтобы предотвратить его обработку в верхнем обработчике.

 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) Событие перехвата щелчка в фазе захвата:

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

... 

const clickOutside$ = fromEvent(document, 'click', { capture: true })
0 голосов
/ 03 июля 2019

Это потому, что вы связали событие клика со всем документом.

из-за того, что ваш клик за пределами счетчика увеличивается.

все, что вам нужно сделать, это проверить событие управления щелчком и затем предотвратить распространение.

пожалуйста, просмотрите эту статью, чтобы понять больше о событиях.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...