【问题标题】:Event to fire when an angular *ngIf statement evaluates in template在模板中评估角度 *ngIf 语句时触发的事件
【发布时间】:2017-11-12 08:32:20
【问题描述】:

如果我有以下情况:

<div *ngIf="user$ | async as user" class="container">
   <p>{{user.name}}</p>
</div>

当上面的div 最终出现在屏幕上时,有什么方法可以执行代码吗?

【问题讨论】:

  • 你不能在代码中订阅用户$ observable吗?
  • 你要执行什么样的代码?
  • 我想隐藏我的加载器(微调器)!我通过使用ReplaySubject 发出布尔值的服务隐藏/显示它。实际的微调器组件放置在应用程序的根组件中,并带有与该布尔值挂钩的 *ngIf 语句。

标签: angular angular-ng-if angular-template


【解决方案1】:

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

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

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

示例 HTML:

<div *ngIf="user$ | async as user" (after-if)="your expression">
   <p>{{user.name}}</p>
</div>

【讨论】:

  • 多年来我无法让它在我的项目中工作,但是当我在顶部的 after-if 周围添加 [ ] 时,我得到了它的工作,例如:@Directive({ selector: '[如果]' })
【解决方案2】:

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

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

1。查看孩子

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

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

<div #userContent *ngIf="user$ | async as user" class="container">
  <p>user.name</p>
</div>

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

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

此解决方案在another question 内部提供,@ViewChild 行为细节也可在here 获得。

2。查看儿童

另一个不使用新指令的解决方案是订阅 @ViewChildren 改变 observable,而不是使用 @ViewChild 这么说:

@ViewChildren('userContent')
private userContent: QueryList<any>;

然后订阅它改变 observable:

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

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


关于 Observables 的注意事项:

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

private $d = new Subject();
ngOnDestroy() {
    this.$d.next();
    this.$d.complete();
}

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

【讨论】:

  • subscribe 函数返回一个订阅,当组件被销毁时(例如在 ngOnDestroy 中),您必须存储对它的引用和取消订阅。有没有办法让它不那么冗长?
  • 有很多方法@Ryan,我最喜欢的是RxJs函数takeUntil,这个答案中有更多细节:stackoverflow.com/a/41177163/1657465
  • 我认为我正在寻找类似于“自定义 ngSubscribe 指令”的东西。 IOW,用于管理订阅的注册和删除(类似于异步管道)。每个订阅的样板代码量是不可取的。您在上面编写的代码不会取消订阅。当组件被销毁时,订阅将继续存在。您可以通过登录订阅来证明这一点。
  • @RyanHaney,感谢您的反馈,我刚刚添加了 unsubscribe 方法,以便开发人员可能不知道所有可观察对象都需要取消订阅
  • 看起来与takeUntil 方法相同,但在扩展类中使用ngOnDestroy 方法
猜你喜欢
  • 1970-01-01
  • 2011-08-10
  • 2014-01-31
  • 2020-05-23
  • 1970-01-01
  • 2019-01-27
  • 1970-01-01
  • 2019-02-10
  • 2017-10-15
相关资源
最近更新 更多