angular - 在 Angular 中发出 http 请求时,图像闪烁并且页面滚动到顶部 - 内部视频
问题描述
视频:https ://vimeo.com/341988489
我有一个包含多个警报的警报页面。每个警报都有一个布尔值“isRead”,指示是否读取警报。橙色信封表示未读取警报。当单击这个橙色信封时,它会变成灰色,并且会向服务器发送一个 http 请求,以将此布尔值从 false 更新为 true。
发生这种情况时,应用程序会出现一些问题: 1. 警报中显示的图像头像“闪烁”,页面滚动到顶部。2. UI 冻结几秒钟。
每当在警报页面组件中向服务器发出 http 请求时,这似乎都会发生。在单击警报组件“信封”并调用 toggleIsRead 时,以及在单击“全部标记为已读”时,如视频所示。
我不知道是什么导致了这个问题。但是,如果这可以提供任何线索,我已经在开发者工具中显示了网络选项卡。
在alerts-page.component.ts 中,我从服务器获取当前用户的警报,并在alert-messages.service.ts 中初始化一个名为messages 的Observable。然后使用它来填充 alertsCreatedToday 和 alertsCreatedAWeekAgo。然后将这些 observable 设置为 alertsCreatedToday$ 和 alertsCreatedAWeekAgo$。
然后用来自这些 Observable 的数据填充每个警报。一张 mat-card 显示每个警报的信息。
当单击警报上的信封以切换布尔值“IsRead”时 - 此布尔值使用乐观更新方法首先更改“alertRecipient-model”上的布尔值,然后通过 http 调用服务器以更新数据库。这是通过 alerts.service.ts 在 users-endpoint.service.ts 中完成的。
我不知道是否需要所有这些信息。也许这个问题有一个简单的解决方案,但我想我不妨提供尽可能多的信息。
我不知道如何找到解决此问题的方法,因为我对可能导致此问题的原因一无所知。
警报-page.component.ts
@Component({
selector: 'alerts-page',
templateUrl: './alerts-page.component.html',
styleUrls: ['./alerts-page.component.scss'],
})
export class AlertsPageComponent implements OnInit {
alertMessages$: Observable<AlertMessage[]>;
alertsCreatedToday$: Observable<Alert[]>;
alertsCreatedAWeekAgo$: Observable<Alert[]>
alertMessagesFromServer: AlertMessage[];
alertMessagesFromClient: AlertMessage[];
alertRecipients: AlertRecipient[];
currentUser: User = new User();
groups: Group[] = [];
users: User[] = [];
newMessages: AlertMessage[];
alertMessages: AlertMessage[];
constructor(private alertMessagesService: AlertMessagesService,
private alertsService: AlertsService,
private notificationMessagesService: NotificationMessagesService,
private dialog: MatDialog,
private usersService: UsersService,
private groupService: GroupsService) { }
ngOnInit() {
this.loadData();
this.initializeObservables();
}
private initializeObservables() {
this.alertMessages$ = this.alertMessagesService.messages;
this.alertsCreatedToday$ = this.alertMessagesService.alertsCreatedToday;
this.alertsCreatedAWeekAgo$ = this.alertMessagesService.alertsCreatedAWeekAgo;
}
private loadData() {
this.currentUser = this.usersService.currentUser;
forkJoin(
this.alertsService.getAlertMessagesForUser(this.currentUser.id),
this.groupService.getGroups(),
this.usersService.getUsers()
).subscribe(
result => this.onDataLoadSuccessful(result[0], result[1], result[2]),
error => this.onDataLoadFailed(error)
);
}
private onDataLoadSuccessful(alertMessagesFromServer: AlertMessage[], groups: Group[], users: User[]) {
this.alertMessagesFromServer = alertMessagesFromServer;
this.groups = groups;
this.users = users;
this.alertMessagesService.messages.subscribe(
(alertMessagesFromClient: AlertMessage[]) => this.alertMessagesFromClient = alertMessagesFromClient
);
if (this.newMessagesFromServer()) {
this.newMessages = _.differenceBy(this.alertMessagesFromServer, this.alertMessagesFromClient, 'id');
this.newMessages.map((message: AlertMessage) => this.alertMessagesService.addMessage(message));
}
}
private onDataLoadFailed(error: any): void {
this.notificationMessagesService.showStickyMessage('Load Error', `Unable to retrieve alerts from the server.\r\nErrors: "${Utilities.getHttpResponseMessage(error)}"`,
MessageSeverity.error, error);
}
private newMessagesFromServer(): boolean {
if (this.alertMessagesFromClient == null && this.alertMessagesFromServer != null) {
return true;
} else if (this.alertMessagesFromServer.length > this.alertMessagesFromClient.length) {
return true;
} else {
return false;
}
}
markAllAsRead() {
this.alertsService.markAlertsAsRead(this.currentUser.id).subscribe(
(alertRecipients: AlertRecipient[]) => {
alertRecipients.map((alertRecipient: AlertRecipient) =>
this.alertMessagesService.markRead(alertRecipient));
}
);
}
}
警报-page.component.html
<button (click)="markAllAsRead()">Mark all as read</button>
<h2>Today</h2>
<ng-container *ngFor="let alert of alertsCreatedToday$ | async">
<alert [alertRecipient]="alert.alertRecipient"[alertMessage]="alert.alertMessage">
</alert>
</ng-container>
<h2>Last Week</h2>
<ng-container *ngFor="let alert of alertsCreatedAWeekAgo$ | async">
<alert [alertRecipient]="alert.alertRecipient"[alertMessage]="alert.alertMessage">
</alert>
</ng-container>
alert.component.ts
@Component({
selector: 'alert',
templateUrl: './alert.component.html',
styleUrls: ['./alert.component.scss'],
})
export class AlertComponent implements OnInit {
@Input() alertRecipient: AlertRecipient;
@Input() alertMessage: AlertMessage;
currentUser: User = new User();
constructor(private dialog: MatDialog,
private alertsService: AlertsService,
private usersService: UsersService,
private alertMessagesService: AlertMessagesService) {
}
ngOnInit() {
this.currentUser = this.usersService.currentUser;
}
getAvatarForAlert(alertMessage: AlertMessage): string {
return require('../../../assets/images/Avatars/' + 'default-avatar.png');
}
toggleIsRead(alertRecipient: AlertRecipient) {
this.alertRecipient.isRead = !alertRecipient.isRead;
this.alertsService.toggleIsRead(alertRecipient)
.subscribe(alertRecipient => {
this.alertMessagesService.toggleRead(alertRecipient);
}, error => {
this.notificationMessagesService.showStickyMessage('Update Error', `An error occured while attempting to mark the alert-message as read.`, MessageSeverity.error, error);
this.alertRecipient.isRead = !alertRecipient.isRead;
});
}
}
alert.component.html
<mat-card>
<mat-card-header>
<div [ngSwitch]="alertRecipient.isRead" (click)="toggleIsRead(alertRecipient)">
<mat-icon *ngSwitchCase="true">drafts</mat-icon>
<mat-icon *ngSwitchCase="false">markunread</mat-icon>
</div>
</mat-card-header>
<mat-card-content>
<div class="avatar-wrapper" fxFlex="25">
<img [src]="getAvatarForAlert(alertMessage)" alt="User Avatar">
</div>
<h3>{{alertMessage.title}}</h3>
<p>{{alertMessage.body}}</p>
</mat-card-content>
<mat-card-actions>
<button>DELETE</button>
<button>DETAILS</button>
</mat-card-actions>
</mat-card>
警报消息.service.ts
const initialMessages: AlertMessage[] = [];
interface IMessagesOperation extends Function {
// tslint:disable-next-line:callable-types
(messages: AlertMessage[]): AlertMessage[];
}
@Injectable()
export class AlertMessagesService {
_hubConnection: HubConnection;
newMessages: Subject<AlertMessage> = new Subject<AlertMessage>();
messages: Observable<AlertMessage[]>;
updates: Subject<any> = new Subject<any>();
create: Subject<AlertMessage> = new Subject<AlertMessage>();
markRecipientAsRead: Subject<any> = new Subject<any>();
toggleReadForRecipient: Subject<any> = new Subject<any>();
alertsCreatedToday: Observable<Alert[]>;
alertsCreatedAWeekAgo: Observable<Alert[]>;
constructor() {
this.initializeStreams();
}
initializeStreams() {
this.messages = this.updates.pipe(
scan((messages: AlertMessage[],
operation: IMessagesOperation) => {
return operation(messages);
}, initialMessages),
map((messages: AlertMessage[]) => messages.sort((m1: AlertMessage, m2: AlertMessage) => m1.sentAt > m2.sentAt ? -1 : 1)),
publishReplay(1),
refCount()
);
this.create.pipe(map(function (message: AlertMessage): IMessagesOperation {
return (messages: AlertMessage[]) => {
return messages.concat(message);
};
}))
.subscribe(this.updates);
this.newMessages
.subscribe(this.create);
this.markRecipientAsDeleted.pipe(
map((recipient: AlertRecipient) => {
return (messages: AlertMessage[]) => {
return messages.map((message: AlertMessage) => {
message.recipients.map((alertRecipient: AlertRecipient) => {
if (alertRecipient.recipientId === recipient.recipientId
&& alertRecipient.alertId === recipient.alertId) {
alertRecipient.isDeleted = recipient.isDeleted;
}
});
return message;
});
};
})
).subscribe(this.updates);
this.markRecipientAsRead.pipe(
map((recipient: AlertRecipient) => {
return (messages: AlertMessage[]) => {
return messages.map((message: AlertMessage) => {
message.recipients.map((alertRecipient: AlertRecipient) => {
if (alertRecipient.recipientId === recipient.recipientId
&& alertRecipient.alertId === recipient.alertId) {
alertRecipient.isRead = true;
}
});
return message;
});
};
})
).subscribe(this.updates);
this.toggleReadForRecipient.pipe(
map((recipient: AlertRecipient) => {
return (messages: AlertMessage[]) => {
return messages.map((message: AlertMessage) => {
message.recipients.map((alertRecipient: AlertRecipient) => {
if (alertRecipient.recipientId === recipient.recipientId
&& alertRecipient.alertId === recipient.alertId) {
alertRecipient.isRead = recipient.isRead;
}
});
return message;
});
};
})
).subscribe(this.updates);
this.alertsCreatedToday = this.messages.pipe(
map((alertMessages: AlertMessage[]) => {
const alerts: Alert[] = [];
alertMessages.map((alertMessage: AlertMessage) => {
alertMessage.recipients.map((alertRecipient: AlertRecipient) => {
if (this.wasCreatedToday(alertMessage)) {
const alert = new Alert(alertRecipient, alertMessage);
alerts.push(alert);
}
});
});
return alerts;
})
);
this.alertsCreatedAWeekAgo = this.messages.pipe(
map((alertMessages: AlertMessage[]) => {
const alerts: Alert[] = [];
alertMessages.map((alertMessage: AlertMessage) => {
alertMessage.recipients.map((alertRecipient: AlertRecipient) => {
if (this.wasCreatedBetweenTodayAndAWeekAgo(alertMessage)) {
const alert = new Alert(alertRecipient, alertMessage);
alerts.push(alert);
}
});
});
return alerts;
})
);
}
addMessage(message: AlertMessage): void {
this.newMessages.next(message);
}
toggleRead(alertRecipient: AlertRecipient): void {
this.toggleReadForRecipient.next(alertRecipient);
}
markRead(recipient: AlertRecipient): void {
this.markRecipientAsRead.next(recipient);
}
wasCreatedToday(alertMessage: AlertMessage): boolean {
const today = moment();
const alertSentAt = moment(alertMessage.sentAt);
return moment(alertSentAt).isSame(today, 'day');
}
wasCreatedBetweenTodayAndAWeekAgo(alertMessage: AlertMessage): boolean {
const today = moment();
const alertSentAt = moment(alertMessage.sentAt);
const oneWeekAgo = moment(moment().subtract(7, 'days'));
return moment(alertSentAt).isBetween(oneWeekAgo, today, 'day');
}
}
export const alertMessagesServiceInjectables: Array<any> = [
AlertMessagesService
];
alerts.service.ts
@Injectable()
export class AlertsService {
constructor(private usersEndpoint: UsersEndpoint) { }
getAlertMessagesForUser(userId: string): Observable<AlertMessage[]> {
return this.usersEndpoint.getAlertMessagesForUserEndpoint<AlertMessage[]>(userId);
}
markAlertsAsRead(userId: string) {
return this.usersEndpoint.getMarkAlertsAsReadEndpoint<AlertRecipient[]>(userId);
}
toggleIsRead(alertRecipient: AlertRecipient) {
return this.usersEndpoint.getToggleIsReadEndpoint<AlertRecipient>(alertRecipient);
}
}
用户端点.service.ts
@Injectable()
export class UsersEndpoint extends EndpointFactory {
private readonly _usersUrl: string = '/api/users';
get usersUrl() { return this.configurations.baseUrl + this._usersUrl; }
constructor(http: HttpClient, configurations: ConfigurationService, injector: Injector) {
super(http, configurations, injector);
}
getAlertMessagesForUserEndpoint<T>(userId: string): Observable<T> {
const endpointUrl = `${this.usersUrl}/${userId}/alertmessages`;
return this.http.get<T>(endpointUrl, this.getRequestHeaders()).pipe<T>(
catchError(error => {
return this.handleError('Unable to get alert-messages for user with id: ' + userId, error, () => this.getAlertMessagesForUserEndpoint(userId));
}));
}
getMarkAlertsAsReadEndpoint<T>(userId: string): Observable<T> {
const endpointUrl = `${this.usersUrl}/${userId}/alertmessages/markallread`;
return this.http.put<T>(endpointUrl, null, this.getRequestHeaders()).pipe<T>(
catchError(error => {
return this.handleError('Unable to mark alertmessages as read for user with id: ' + userId, error, () => this.getMarkAlertsAsReadEndpoint(userId));
}));
}
getToggleIsReadEndpoint<T>(alertRecipient: AlertRecipient): Observable<T> {
const endpointUrl = `${this.usersUrl}/${alertRecipient.recipientId}/alertmessages/${alertRecipient.alertId}/toggleread`;
return this.http.patch<T>(endpointUrl, JSON.stringify(alertRecipient), this.getRequestHeaders()).pipe<T>(
catchError(error => {
return this.handleError('Unable to toggle isRead-status for alert-message to user with id: ' + alertRecipient.recipientId, error, () => this.getToggleIsReadEndpoint(alertRecipient));
}));
}
getMarkAlertRecipientAsDeletedEndpoint<T>(alertRecipient: AlertRecipient): Observable<T> {
const endpointUrl = `${this.usersUrl}/${alertRecipient.recipientId}/alertmessages/${alertRecipient.alertId}/markdeleted`;
return this.http.patch<T>(endpointUrl, JSON.stringify(alertRecipient), this.getRequestHeaders()).pipe<T>(
catchError(error => {
return this.handleError('Unable to mark alert-message as deleted', error, () => this.getMarkAlertRecipientAsDeletedEndpoint(alertRecipient));
}));
}
}
解决方案
我认为它正在发生,因为您的消息被重新呈现是因为每次发出新值时alertsCreatedToday$
,可观察对象都会发出新值。您希望刷新警报组件上的数据,而不是重新渲染。为此,您应该使用. 让我们像这样更改代码 -alertsCreatedAWeekAgo$
AlertMessagesService.update Subject
trackBy
今天
<ng-container *ngFor="let alert of alertsCreatedToday$ | async; trackBy: trackByFn1">
<alert [alertRecipient]="alert.alertRecipient"[alertMessage]="alert.alertMessage">
</alert>
</ng-container>
<h2>Last Week</h2>
<ng-container *ngFor="let alert of alertsCreatedAWeekAgo$ | async; trackBy: trackByFn2">
<alert [alertRecipient]="alert.alertRecipient"[alertMessage]="alert.alertMessage">
</alert>
</ng-container>
在你export class AlertsPageComponent
有两个这样的功能 -
trackByFn1(item, index) {
//return the id of your alert
return item.alertId;
}
trackByFn2(item, index) {
//return the id of your alert
return item.alertId;
}
有关更多详细信息,请参阅 - https://netbasal.com/angular-2-improve-performance-with-trackby-cc147b5104e5。
推荐阅读
- python - “尝试相对导入超出顶级包”
- jquery - JQuery 日期选择器在 Laravel 的动态输入表单中不起作用
- laravel - Laravel BelongsToMany 与 WhereHas 条件的关系
- reactjs - 我可以在上下文对象中传递 redux 动作创建者吗?
- oauth-2.0 - Acumatica 在刷新 OAuth 2 访问令牌时抛出 500 服务器异常
- javascript - 如何从承诺中获取数据?
- amazon - 亚马逊 MWS API 不再返回个人信息
- mysql - 如何使用 Angular 防止多对多表中的重复实体
- javascript - 简单的队列系统在 javascript 中不起作用
- android - Android Chrome 中的静默打印