点击Angular组件外部。

huangapple go评论70阅读模式
英文:

Click outside angular component

问题

我知道关于这个问题有无数的问题,我尝试了每一种解决方法,但都不适合我的需求。尝试了指令,尝试了直接在我的组件中使用,但一次都没有成功。

所以我的情况是这样的;我有一个表格视图,在每个表格视图的末尾,您可以点击一个图标来打开一个小的下拉菜单。弹出的下拉菜单是一个小组件。所以在我的父组件中,它看起来像这样:

<tbody>
    <tr *ngFor="let rows of subscriptionTable.data; let i = index;">
        <td *ngFor="let cell of subscriptionTable.data[i].data"> 
            <!-- 表格操作 -->
            <div *ngIf="cell.actions && cell.actions.length > 0">
                <app-dropdown
                    [actions]         = cell.actions
                    (onActionClicked) = "handleSubscriptionAction($event, i)"
                >
                </app-dropdown>
            </div>
        </td>
    </tr>
</tbody>

所以我正在渲染多个子级app-dropdown组件,看起来像这样:

<div class="fs-action">
  <div class="fs-action__dots" (click)="openActionSheet($event)">
    <span class="fs-action__dot"></span>
    <span class="fs-action__dot"></span>
    <span class="fs-action__dot"></span>
  </div>

  <ul class="fs-action__list">
    <li 
      class="fs-action__item"
      *ngFor="let action of actions; let i = index;" 
      (click)="actionClicked( $event, i )" 
      [ngClass]="{'fs-action__item--danger': action.type === 'destructive' }"
    >
        {{ action.label }}
    </li>
  </ul>
</div>

现在我想做的是,只要我点击fs-action元素外部,下拉菜单就会关闭。我想在app-dropdown组件内部执行此操作,然后与此组件相关的所有内容都在同一个位置。

但是,一旦我附加了一个指令或其他内容,hostlistener 就会为每一行添加。然后每次点击外部都会触发多次。检查我是否点击在操作元素外部就变得困难,因为它然后会为所有其他的hostlisteners 渲染为false,并且对于处于活动状态的那个来说是true。

问题在这个stackblitz中有示例:https://stackblitz.com/edit/angular-xjdmg4

我在dropdown.component.ts中注释了引发问题的部分;

  public onClickOutside( event ) {
    // 它执行3次,对于每个添加的项都会执行
    // 即使在下拉菜单内部点击,也会调用此函数
    console.log("click")
  }
英文:

I know there are countless of questions around this, and I tried every solution but nothing suited my needs. Tried directives, tried it straight in my component but it didn't work a single time.

So my situation is like this; I have a tableview and at the end of every tableview you can open a small dropdown by clicking on an icon. The dropdown that pops up is a small component. So in my parent component it looks like this;

<tbody>
    <tr *ngFor="let rows of subscriptionTable.data; let i = index;">
        <td *ngFor="let cell of subscriptionTable.data[i].data"> 
            <!-- Table actions -->
            <div *ngIf="cell.actions && cell.actions.length > 0">
                <app-dropdown
                    [actions]         = cell.actions
                    (onActionClicked) = "handleSubscriptionAction($event, i)"
                >
                </app-dropdown>
            </div>
        </td>
    </tr>
</tbody>

So I'm rendering multiple child app-dropdown components that look like this;

<div class="fs-action">
  <div class="fs-action__dots" (click)="openActionSheet($event)">
    <span class="fs-action__dot"></span>
    <span class="fs-action__dot"></span>
    <span class="fs-action__dot"></span>
  </div>

  <ul class="fs-action__list">
    <li 
      class="fs-action__item"
      *ngFor="let action of actions; let i = index;" 
      (click)="actionClicked( $event, i )" 
      [ngClass]="{'fs-action__item--danger': action.type === 'destructive' }"
    >
        {{ action.label }}
    </li>
  </ul>
</div>

What I want to do now is, that as soon as I click outside the fs-action element, the dropdown dismisses. I would like to do that inside the app-dropdown component, then everything related to this component is in the same place.

But as soon as I attach a directive or anything else, the hostlistener get's added for every row. Every outside click is then being triggered multiple times. Checking if I was clicking outside the action element then becomes a burden, because it then renders false for all the other hostlisteners and true for the one that's active.

The issue is illustrated inside this stackblitz; https://stackblitz.com/edit/angular-xjdmg4

I commented the section which causes issues inside dropdown.component.ts;

  public onClickOutside( event ) {
    // It executes 3 times, for each of the items that is added
    // Even when clicking inside the dropdown, it calls this function
    console.log("click")
  }

答案1

得分: 2

只需使用(click)事件绑定,传递来自组件实例的函数,就可以完成。

解决方案:
声明指令ClickOutside如下:

@Directive({
  selector: '[clickOutside]',
})
export class ClickOutsideDirective {
  @Output('onClickOutside') onClickOutside = new EventEmitter<MouseEvent>();

  constructor(private _eref: ElementRef) {}

  @HostListener('document:click', ['$event', '$event.target'])
  onDocumentClicked(event: MouseEvent, targetElement: HTMLElement) {
    if (targetElement && document.body.contains(targetElement) && !this._eref.nativeElement.contains(targetElement)) {
      this.onClickOutside.emit(event);
    }
  }
}
<div clickOutside (onClickOutside)="onClickOutside()">
...
</div>

还有一个在npm模块中称为ng-click-outside的指令。

安装: npm install --save ng-click-outside

在下面的链接中阅读完整文档:

ng-click-outside指令

英文:

Just use the (click) event binding, pass a function from the component’s instance and you are done.

Solution:
Declare Directive ClickOutside like below:

@Directive({
  selector: &#39;[clickOutside]&#39;,
})
export class ClickOutsideDirective {
  @Output(&#39;onClickOutside&#39;) onClickOutside = new EventEmitter&lt;MouseEvent&gt;();

  constructor(private _eref: ElementRef) {}

  @HostListener(&#39;document:click&#39;, [&#39;$event&#39;, &#39;$event.target&#39;])
  onDocumentClicked(event: MouseEvent, targetElement: HTMLElement) {
    if (targetElement &amp;&amp; document.body.contains(targetElement) &amp;&amp; !this._eref.nativeElement.contains(targetElement)) {
      this.onClickOutside.emit(event);
      }
    }
  }
}
&lt;div clickOutside (onClickOutside)=&quot;onClickOutside()&quot;&gt;
...
&lt;/div&gt;

And there is directive in an npm module called ng-click-outside.

Installation: npm install --save ng-click-outside

Read full documentation in link below:

ng-click-outside directive

答案2

得分: 1

以下是翻译好的部分:

我使用了一个指令来处理类似的情况。
安装:

npm install --save ng-click-outside

然后在你的模块中导入:

import { ClickOutsideModule } from 'ng-click-outside';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, ClickOutsideModule],
  bootstrap: [AppComponent]
})
class AppModule {}

在你的组件中:

@Component({
  selector: 'app',
  template: `
    <div (clickOutside)="onClickedOutside($event)">点击外部</div>
  `
})
export class AppComponent {
  onClickedOutside(e: Event) {
    console.log('点击外部:', e);
  }
}

它还具有一些选项,你可以在 https://www.npmjs.com/package/ng-click-outside 上查看。

关于

英文:

I used a directive to a similar situation.
To install:

npm install --save ng-click-outside

Then import in your module

import { ClickOutsideModule } from &#39;ng-click-outside&#39;;

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, ClickOutsideModule],
  bootstrap: [AppComponent]
})
class AppModule {}

In your component

@Component({
  selector: &#39;app&#39;,
  template: `
    &lt;div (clickOutside)=&quot;onClickedOutside($event)&quot;&gt;Click outside this&lt;/div&gt;
  `
})
export class AppComponent {
  onClickedOutside(e: Event) {
  console.log(&#39;Clicked outside:&#39;, e);
  }
}

It has some options. You can see it in https://www.npmjs.com/package/ng-click-outside

Regards

huangapple
  • 本文由 发表于 2020年1月4日 01:13:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/59582637.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定