异步pypes不能“刷新”模板,但可以使用subscription + detectChanges()起作用吗?

时间:2019-09-02 09:12:47

标签: angular error-handling notifications angular-animations

我正在用角度代码示例创建简单的项目,但是由于某些原因-除非出现用户焦点输入或单击其他地方(例如GlobalErrorHandler),否则不会出现全局错误(来自buttons)。在Global Notifications页上,如果用户选择了任何通知示例-它会立即显示。但是在Login页中,如果用户提交了表单->错误,并且除非集中输入/单击按钮,否则不会显示。

我怀疑 GlobalErrorHandler (global-error-handler.service.ts)中存在此问题。我认为,当服务尝试handleError()时,它会以某种方式“滞后/冻结”更改,因为在 GlobalNotificationsService 服务中,我正在更新该通知列表,但是在{{1}中}没有创建DOM元素/异步管道在创建时不响应新值。也许问题出在global-notifications.component.html上,或者injection's不能正常工作:?

项目可以在这里找到: https://github.com/dirdakas/ng-playground

global-error-handler.service.ts

async pipes

global-notifications.service.ts

@Injectable({
  providedIn: 'root'
})
export class GlobalErrorHandler extends ErrorHandler {
  TimersEnum = TimersEnum;

  constructor(
    // Because the ErrorHandler is created before the providers, we’ll have to use the Injector to get them.
    private injector: Injector,
  ) {
    super();
  }

  handleError(error: Error | HttpErrorResponse) {
    const globalNotificationsService = this.injector.get(GlobalNotificationsService);
    const errorsService = this.injector.get(ErrorsService);
    const router = this.injector.get(Router);
    console.log('error', error);

    if (error instanceof HttpErrorResponse) {
    // Server error happened      
      if (!navigator.onLine) {
        // No Internet connection
        return globalNotificationsService.addTypedNotification(
          'No Internet connection',
          NotificationTypeEnum.info,
          TimersEnum.medium
        );
      }

      globalNotificationsService.addTypedNotification(
        error.statusText,
        NotificationTypeEnum.error,
        TimersEnum.medium
      );
    }
  }
}

global-notifications.component.html 在管道上的此组件上,我可以看到@Injectable({ providedIn: 'root' }) export class GlobalNotificationsService { private notificationsSubject = new BehaviorSubject(EMPTY_NOTIFICATIONS); notifications$: Observable<IGlobalNotification[]> = this.notificationsSubject.asObservable(); constructor(private animationBuilder: AnimationBuilder) {} addSimpleNotification(message: string): void { const notification: IGlobalNotification = { id: 0, message: message, isUltimante: false, isClosable: true, type: null }; this.addNotification(notification); } addTypedNotification( message: string, type: NotificationTypeEnum, closeAfter: number = null ): void { const notification: IGlobalNotification = { id: 0, message: message, isUltimante: false, isClosable: true, type: type, closeAfter: closeAfter }; this.addNotification(notification); } removeNotificationById(id: number): void { let newNotificationList = this.notificationsSubject.getValue(); newNotificationList = newNotificationList .filter((notification: IGlobalNotification) => { return notification.id !== id; }); this.updateNotificationList(newNotificationList); } getCreationAnimation(notification: IGlobalNotification): AnimationFactory {... } getRemovalAnimation(): AnimationFactory {...} addNotification(notification: IGlobalNotification): void { const newNotificationList = this.notificationsSubject.getValue(); const lastNotificationInList = this.getLastNotification(); const id = lastNotificationInList ? (lastNotificationInList.id + 1) : 0; const newMessage: IGlobalNotification = { id: id, isClosable: notification.isClosable, message: notification.message, type: notification.type, isUltimante: notification.isUltimante, cancelButtonText: notification.cancelButtonText, cancelFunction: notification.cancelFunction, closeAfter: notification.closeAfter, confirmButtonText: notification.confirmButtonText, confirmFunction: notification.confirmFunction, linkTo: notification.linkTo, hasInput: notification.hasInput }; newNotificationList.push(newMessage); this.updateNotificationList(newNotificationList); } getNotificationById(id: number): IGlobalNotification | null { const currentNotifications: IGlobalNotification[] = this.notificationsSubject.getValue(); if (currentNotifications.length > 0) { const notification: IGlobalNotification = currentNotifications.find((el: IGlobalNotification) => { return el.id === id; }); if (notification) { return notification; } } return null; } updateNotificationList(newList: IGlobalNotification[]): void {...} private getLastNotification(): IGlobalNotification {...} private getNotificationColors(notification: IGlobalNotification): INotificationColors {...} } 已更新,但是由于某些原因,直到按钮/输入click(elsewhere)之前,temlate才会刷新

notifications

global-typed-notification.component.ts

<div *ngIf="notifications$ | async as notifications"
    class="global-notifications">
    <div *ngFor="let notification of notifications">
        <app-global-simple-notification *ngIf="!notification.isUltimante && !notification.type" [notification]="notification"></app-global-simple-notification>
        <app-global-typed-notification *ngIf="!notification.isUltimante && notification.type" [notification]="notification"></app-global-typed-notification>
        <app-global-notification *ngIf="notification.isUltimante" [notification]="notification"></app-global-notification>
    </div>
</div>

我希望@Component({ selector: 'app-global-typed-notification', templateUrl: './global-typed-notification.component.html', styleUrls: ['./global-typed-notification.component.scss'] }) export class GlobalTypedNotificationComponent implements OnInit { @Input() notification: IGlobalNotification; @ViewChild('notificationWrapper', { static: true }) _self: ElementRef; animations: IAnimationStates = { create: null, remove: null }; constructor( private globalNotificationsService: GlobalNotificationsService ) {} ngOnInit(): void { this.createAnimations(); this.playAnimation('create'); this.checkIfAutoClosing(); } closeNotification(id: number): void { this.playAnimation('remove'); setTimeout(() => { this.globalNotificationsService.removeNotificationById(id); }, 500); } private checkIfAutoClosing(): void { if (this.notification.closeAfter) { setTimeout(() => { this.closeNotification(this.notification.id); }, this.notification.closeAfter); } } private createAnimations(): void { this.animations.create = this.globalNotificationsService.getCreationAnimation(this.notification); this.animations.remove = this.globalNotificationsService.getRemovalAnimation(); } private playAnimation(type: string): void { let player: AnimationPlayer; if (type === 'create') { player = this.animations.create.create(this._self.nativeElement); } else if (type === 'remove') { player = this.animations.remove.create(this._self.nativeElement); } player.play(); } } Global error notifications页面中的行为相同:在创建时显示(如果发生错误->立即显示,而无需用户集中输入或单击按钮)

更新:

在进一步研究之后,我应用了这些更改,现在它开始起作用:

global-notifications.component.html

Global Notifications

global-notifications.component.ts

<div *ngIf="notifications.length > 0"
    class="global-notifications">
  <div *ngFor="let notification of notifications">
    <app-global-simple-notification *ngIf="!notification.isUltimante && !notification.type" [notification]="notification" ></app-global-simple-notification>
    <app-global-typed-notification *ngIf="!notification.isUltimante && notification.type" [notification]="notification"> </app-global-typed-notification>
    <app-global-notification *ngIf="notification.isUltimante" [notification]="notification"> </app-global-notification>
  </div>
</div>

但是我还是不明白,为什么简单的@Component({ selector: 'app-global-notifications', templateUrl: './global-notifications.component.html', styleUrls: ['./global-notifications.component.scss'] }) export class GlobalNotificationsComponent implements OnInit, OnDestroy { notificationsSub: Subscription; notifications: IGlobalNotification[] = []; constructor( private globalNotificationsService: GlobalNotificationsService, private cd: ChangeDetectorRef ) {} ngOnInit(): void { this.notificationsSub = this.globalNotificationsService.notifications$ .pipe().subscribe((data) => { this.notifications = data; this.cd.detectChanges(); }); } ngOnDestroy(): void { this.notificationsSub.unsubscribe(); } } 不会更新模板?为什么我们需要订阅并强制使用| async

0 个答案:

没有答案