当角度*ngIf语句在模板中求值时要触发的事件

Sam*_*mmy 12 angular-template angular-ng-if angular

如果我有以下内容:

<div *ngIf="user$ | async as user" class="container">
  <p>user.name</p>
</div>
Run Code Online (Sandbox Code Playgroud)

div上面的内容最终出现在屏幕上时,有没有办法可以执行代码?

Rea*_*lar 14

ngIf将删除该DOM元素和所有附加的组件/指令.因此,您可以编写一个简单的指令,在第一次创建时执行该事件.当ngIf从false转换为true时,将创建指令(再次,等等......)

@Directive({selector: '[after-if]'})
export class AfterIfDirective implements AfterContentInit {
    @Output('after-if')
    public after: EventEmitter<AfterIfDirective> = new EventEmitter();

    public ngAfterContentInit(): void {
        setTimeout(()=>{
           // timeout helps prevent unexpected change errors
           this.after.next(this);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

示例HTML:

<div *ngIf="user$ | async as user" class="container" (after-if)="your expression">
    <p>user.name</p>
</div>
Run Code Online (Sandbox Code Playgroud)


Lui*_*mas 12

不创建新指令的解决方案是利用@ViewChild@ViewChildren行为:

配置视图查询的属性装饰器。变更检测器在视图 DOM 中查找与选择器匹配的第一个元素或指令。如果视图 DOM 发生变化,并且新的子级与选择器匹配,则该属性会更新。

1.ViewChild

重要的部分是如果视图 DOM 发生变化,这意味着在这种情况下这只会在创建或销毁元素时触发。

首先为元素声明一个变量名,对于我使用的样本 #userContent

<div #userContent *ngIf="user$ | async as user" class="container">
  <p>user.name</p>
</div>
Run Code Online (Sandbox Code Playgroud)

然后@ViewChild在你的组件中添加一个引用:

@ViewChild('userContent') set userContent(element) {
  if (element) {
     // here you get access only when element is rendered (or destroyed)
  }
}
Run Code Online (Sandbox Code Playgroud)

此解决方案是在另一个问题@ViewChild中提供的,此处还提供行为详细信息。

2.ViewChildren

另一个不使用新指令的解决方案是订阅 @ViewChildren更改 observable,而不是@ViewChild像这样使用put :

@ViewChildren('userContent')
private userContent: QueryList<any>;
Run Code Online (Sandbox Code Playgroud)

然后订阅它改变可观察:

userContent.changes.pipe(takeUntil(this.$d)).subscribe((d: QueryList<any>) => {
  if (d.length) {
    // here you get access only when element is rendered
  }
});
Run Code Online (Sandbox Code Playgroud)

我更喜欢最后一种方式,因为对我来说,处理 observables 比内部验证更容易setter's,而且这种方法更接近“事件”概念。


关于 Observable 的注意事项:

所有 observable 都需要取消订阅,否则会引发内存泄漏;有很多方法可以防止这种情况发生,作为建议,我最喜欢的方法是 RxJs 函数takeUntil,这部分:pipe(takeUntil(this.$d))以及您的ngOnDestroy方法中的以下内容:

private $d = new Subject();
ngOnDestroy() {
    this.$d.next();
    this.$d.complete();
}
Run Code Online (Sandbox Code Playgroud)

我推荐这种方式的原因是因为实现它的额外代码量非常低;您可以对组件 ( this.$d)中的所有订阅使用相同的变量。有关取消订阅方法的更多详细信息/选项,请参阅此其他相关问题/答案