【发布时间】:2021-11-27 19:38:34
【问题描述】:
我目前正在创建自己的 toastr 服务,如下面的 GIF 所示
我想要实现的目标 https://stackblitz.com/edit/angular-ivy-tgm4st?file=src/app/app.component.ts 但没有查询选择器。根据我的阅读,您不应该使用 queryselector 以角度检索 DOM 中的元素
问题 每当我单击 CTA 按钮时,我都会将一个 toast 元素添加到一个 toast 数组中,该组件已订阅并用于更新 DOM。
toast 是这样生成的:
export class ToastComponent implements OnInit {
constructor(private toast: ToastService, protected elementRef: ElementRef) {}
toasts = this.toast.Toasts;
<div
class="toast-wrapper wobble-animation"
*ngFor="let t of toasts.value"
(click)="DestroyToast(t, $event)"
我想要什么 每当“animationend”销毁 HTML 元素时,我想在 toast 中添加一个事件监听器。我已经通过点击这行代码来做到这一点:
DestroyToast(element, event): void {
event.target.classList.remove('wobble-animation');
event.target.classList.add('slide-out-animation');
event.target.addEventListener('animationend', () => {
this.toasts.value.splice(this.toasts.value.indexOf(element), 1);
});
}
我最初的想法是订阅数组并将其用作事件监听器,以便在推送某些内容时使用。然后我会使用一个函数来获取最新的 toast 并添加另一个事件监听器,即“animationend”。
我试过这样的方法:
ngOnInit(): void {
this.toast.Toasts.subscribe((args) => {
this.UpdateToasts();
});
}
UpdateToasts() {
let toastElements = document.querySelectorAll('.toast');
console.log(toastElements);
}
但不幸的是它太慢了,并且总是在第一个事件时返回 null。
我认为我已经阅读过在 Angular 中使用 querySelector 通常是不好的做法。所以问题是:
如何在没有querySelector的情况下在Angular中获取动态生成的元素?
完整代码
Toast.Component.ts
import { ToastService } from './../../services/toast.service';
import { toast } from './toast.model';
import { Component, OnInit, ElementRef } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
selector: 'app-toast',
templateUrl: './toast.component.html',
styleUrls: ['./toast.component.scss'],
})
export class ToastComponent implements OnInit {
constructor(private toast: ToastService, protected elementRef: ElementRef) {}
toasts = this.toast.Toasts;
ngOnInit(): void {
this.toast.Toasts.subscribe((args) => {
this.UpdateToasts();
});
}
ngOnDestroy() {
this.toasts.unsubscribe();
}
DestroyToast(element, event): void {
event.target.classList.remove('wobble-animation');
event.target.classList.add('slide-out-animation');
event.target.addEventListener('animationend', () => {
this.toasts.value.splice(this.toasts.value.indexOf(element), 1);
});
}
UpdateToasts() {
let toastElements = document.querySelectorAll('.toast');
console.log(toastElements);
}
}
Toast.Component.html
<div class="toast-container">
<div
class="toast-wrapper wobble-animation"
*ngFor="let t of toasts.value"
(click)="DestroyToast(t, $event)"
>
<div
class="toast default"
[ngClass]="{ 'slide-out-animation': t.TimeLeft < 1 }"
>
<div class="notification-count" *ngIf="t.Count > 1">
{{ t.Count }}
</div>
<div class="content-container">
<p class="title">
{{ t.Title }}
</p>
<p class="content">{{ t.Content }}</p>
</div>
<span class="progress">
<span
class="real-progress"
[ngStyle]="{ 'width.%': t.PercentageCompleted }"
></span>
</span>
</div>
</div>
</div>
Toast.Service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { toast } from '../components/toast/toast.model';
@Injectable({
providedIn: 'root',
})
export class ToastService {
public Toasts = new BehaviorSubject<Array<object>>([]);
constructor() {}
Toast(Title: string, Message?: string, Style?: string, Timer?: number) {
const toastModel = new toast({
Title: Title,
Content: Message,
Timer: Timer,
Style: Style,
TimeLeft: Timer,
Count: 1,
PercentageCompleted: 100,
});
this.AddToast(toastModel);
}
private AddToast(toast: toast) {
const currentArr = this.Toasts.value;
const updatedToast = [...currentArr, toast];
let timer = setInterval(function () {
toast.PercentageCompleted = toast.TimeLeft / (toast.Timer / 100);
toast.TimeLeft = toast.TimeLeft - 10;
if (toast.TimeLeft <= 0 || !toast.TimeLeft) {
clearInterval(timer);
}
}, 10);
this.Toasts.next(updatedToast);
}
}
使用实时代码链接到网站ModernnaMedia
【问题讨论】:
-
请添加一个最小代码stackblitz 示例以显示您的用例,这样每个人都可以更轻松地提供解决方案,顺便说一句。 ViewChildren 应该足以解决您的问题,您只需将您的逻辑移动到
ngOnChanges以防止空引用 -
@LuisLimas 谢谢!我下班后会调查的。我会让你知道它是怎么回事:)
-
@LuisLimas 我已经更新并添加了 stackbiz!
标签: javascript angular typescript angular-universal