视频:https://vimeo.com/341988489
我有一个警报页面,其中包含多个警报。每个警报都有一个布尔值“ isRead”,指示是否已读取警报。橙色信封表示未读取警报。单击此橙色信封后,它将变为灰色,并且向服务器发送了一个http请求,以将该布尔值从false更新为true。
发生这种情况时,应用程序中会出现一些问题: 1.在警报“闪烁”中显示的图像头像,页面滚动到顶部。 2.用户界面冻结了几秒钟。
每当对alerts-page-component中的服务器发出http请求时,似乎就会发生这种情况。如在视频中所示,当单击警报组件的“信封”并调用toggleIsRead时,以及单击“全部标记为已读”时,都是如此。
我不知道是什么导致了此问题。但是我已经在develoer工具中显示了network-tab,如果这可以提供任何线索的话。
在alerts-page.component.ts中,我正在从服务器获取当前用户的警报,并在alert-messages.service.ts中初始化一个名为Observable的消息。然后将其用于填充alertCreatedToday和alertCreatedAWeekAgo。然后将这些可观察值设置为alertCreatedToday $和alertCreatedAWeekAgo $。
然后,每个警报都填充有来自这些Observables的数据。席卡正在显示每个警报的信息。
当单击警报的信封以切换布尔值“ IsRead”时,此布尔值将使用乐观更新方法来首先更改“ alertRecipient-model”上的布尔值,然后通过http调用服务器以更新数据库。这是通过Alerts.service.ts在users-endpoint.service.ts中完成的。
我不知道是否需要所有这些信息。也许有解决这个问题的简单方法,但是我认为我可能会提供尽可能多的信息。
我不知道如何找到该问题的解决方案,因为我对可能导致它的原因一无所知。
alerts-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));
}
);
}
}
alerts-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>
alert-messages.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);
}
}
users-endpoint.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));
}));
}
}
答案 0 :(得分:0)
我认为之所以会这样,是因为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,以获取更多详细信息。