在Angular中发出http请求时,图像闪烁并且页面滚动到顶部-视频内部

时间:2019-06-13 10:13:11

标签: angular http rxjs angular-material

视频: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));
            }));
    }

}

1 个答案:

答案 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,以获取更多详细信息。