角度:从可管道运算符将项目推送到数组时,组件不会更新DOM

时间:2018-08-20 21:49:39

标签: typescript angular5 angular-cli

组件

@Component({
selector: 'alert-queue',
templateUrl: './alert-queue.component.html',
styleUrls: ['./alert-queue.component.scss']
})

export class AlertQueueComponent implements OnInit, OnDestroy {
public alerts: Alert[];
private alerts$: Subscription;
private alertsComplete$: Subject<boolean> = new Subject<boolean>();

constructor(private alertService: AlertService, private ref: ChangeDetectorRef) { }

ngOnInit() {
    this.alerts = [];

    this.alertService
        .getAlertStream()
        .pipe(
            tap(alert => {
                console.log(`alert tapped before delay: ${alert.message}`);
                this.alerts.push(alert);
                console.log(`${this.alerts.length}`);
                this.alerts = this.alerts.slice();
                this.ref.markForCheck();
            }),
            delay(5800),
            tap(alert => {
                console.log(`alert tapped after delay: ${alert.message}`);
                _.remove(this.alerts, { id: alert.id });
                console.log(`${this.alerts.length}`);
                this.alerts = this.alerts.slice();
                this.ref.markForCheck();
            }),             
            //catchError((err, caught) => {
            //  console.error(`error in alert stream: ${err}`);
            //  return caught;
            //}),
            takeUntil(this.alertsComplete$)
        )
        .subscribe();
}

ngOnDestroy() {
    this.alertsComplete$.next(true);
    this.alertsComplete$.unsubscribe();
}

组件模板

<div class="alert-queue">   
    <div *ngFor="let alert of alerts | reverse">
        <alert [alert]="alert"></alert>
    </div>
</div>

警报组件(可能不相关)

<div [ngClass]="class" [@effect]="state">
    <strong>{{typeDisplay()}}:</strong> {{alert.message}}
</div>

警报服务

@Injectable()
export class AlertService {
private alerts: Subject<Alert> = new Subject();

public getAlertStream(...types: AlertType[]): Observable<Alert> {
    if (types.length > 0) {
        return this.alerts.pipe(
            filter(alert => _.some(types, t => t === alert.type))
        );
    }
    else {
        return this.alerts;
    }
}

public push(message: string, type?: AlertType, options?: object): void {
    if (_.isEmpty(message)) {
        console.warn('Cannot push an alert with blank message, the notification was cancelled');
    } else {
        var alert = this.createAlert(message, (type || AlertType.Default), (options || {}));
        this.alerts.next(alert);
    }
}

private createAlert(message: string, type: AlertType, options: object): Alert {
    return new Alert(message, type, options);
}   

我尝试了许多不同的操作,但似乎无法弄清楚我在做什么错。当我从由按钮触发的常规功能推送到警报服务时,队列会更新并设置动画效果。

当它是对HTTP请求的订阅时,除非我以某种方式与DOM交互(键入输入或单击按钮,然后警报出现并刷新视图),否则屏幕根本不会更新。忽略我使用ChangeDetectorRef的地方,这是最后的尝试,试图使DOM识别数组更新。

我应该指出点击操作符中的日志消息确实出现在控制台中。严格来说,这似乎是DOM更新的问题,我无法忍受。


已解决

显然,tap方法中的回调发生在Angular上下文之外。一个有效的解决方案是将NgZone注入构造函数,并使用zone.run(...)包装回调。我也将其移到了subscription方法。

起作用的代码是:

export class AlertQueueComponent implements OnInit, OnDestroy {
public alerts: Alert[];
private alerts$: Subscription;
private alertsComplete$: Subject<boolean> = new Subject<boolean>();

constructor(private alertService: AlertService, private zone: NgZone) { }

ngOnInit() {
    this.alerts = [];

    this.alertService
        .getAlertStream()
        .pipe(              
            takeUntil(this.alertsComplete$)
        )
        .subscribe(alert => {
            this.zone.run(() => {
                console.log(`alert subscribe success before delay: ${alert.message}`);
                this.alerts.push(alert);

                setTimeout(() => {
                    console.log(`alert subscribe after before delay: ${alert.message}`);
                    _.remove(this.alerts, { id: alert.id });
                }, 5800);
            }, this);
        });     
}

ngOnDestroy() {
    this.alertsComplete$.next(true);
    this.alertsComplete$.unsubscribe();
}

public checkArray() {
    console.log(`Array has ${this.alerts.length} elements`);
}

public getAlertClass(alert: Alert) {
    return `alert alert-${alert.typeClass()}`;
}
}

0 个答案:

没有答案