我正在用角度代码示例创建简单的项目,但是由于某些原因-除非出现用户焦点输入或单击其他地方(例如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
?