fixed an issue where a rehydrate would sometimes leave the app in a broken state

This commit is contained in:
Art Lowel
2017-10-26 12:58:42 +02:00
parent 94272b9b99
commit ee84399d23
7 changed files with 41 additions and 36 deletions

View File

@@ -36,16 +36,16 @@
<ds-error *ngIf="(collectionData | async)?.hasFailed" message="{{'error.collection' | translate}}"></ds-error>
<ds-loading *ngIf="(collectionData | async)?.isLoading" message="{{'loading.collection' | translate}}"></ds-loading>
<br>
<div *ngIf="itemData?.hasSucceeded" @fadeIn>
<div *ngIf="(itemData | async)?.hasSucceeded" @fadeIn>
<h2>{{'collection.page.browse.recent.head' | translate}}</h2>
<ds-object-list
[config]="paginationConfig"
[sortConfig]="sortConfig"
[objects]="itemData"
[objects]="(itemData | async)"
[hideGear]="false"
(paginationChange)="onPaginationChange($event)">
</ds-object-list>
</div>
<ds-error *ngIf="itemData?.hasFailed" message="{{'error.recent-submissions' | translate}}"></ds-error>
<ds-loading *ngIf="!itemData || itemData.isLoading" message="{{'loading.recent-submissions' | translate}}"></ds-loading>
<ds-error *ngIf="(itemData | async)?.hasFailed" message="{{'error.recent-submissions' | translate}}"></ds-error>
<ds-loading *ngIf="!(itemData | async) || (itemData | async).isLoading" message="{{'loading.recent-submissions' | translate}}"></ds-loading>
</div>

View File

@@ -30,7 +30,7 @@ import { PaginationComponentOptions } from '../shared/pagination/pagination-comp
})
export class CollectionPageComponent implements OnInit, OnDestroy {
collectionData: Observable<RemoteData<Collection>>;
itemData: RemoteData<Item[]>;
itemData: Observable<RemoteData<Item[]>>;
logoData: Observable<RemoteData<Bitstream>>;
paginationConfig: PaginationComponentOptions;
sortConfig: SortOptions;
@@ -87,14 +87,12 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
}
updatePage(searchOptions) {
this.subs.push(this.itemDataService.findAll({
this.itemData = this.itemDataService.findAll({
scopeID: this.collectionId,
currentPage: searchOptions.pagination.currentPage,
elementsPerPage: searchOptions.pagination.pageSize,
sort: searchOptions.sort
}).subscribe((rd: RemoteData<Item[]>) => {
this.itemData = rd;
}));
});
}
ngOnDestroy(): void {

View File

@@ -1,32 +1,32 @@
import {
ChangeDetectionStrategy,
Component,
HostListener,
Inject,
ViewEncapsulation,
OnInit,
HostListener, SimpleChanges, OnChanges
ViewEncapsulation
} from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Store } from '@ngrx/store';
import { TransferState } from '../modules/transfer-state/transfer-state';
import { HostWindowState } from './shared/host-window.reducer';
import { HostWindowResizeAction } from './shared/host-window.actions';
import { NativeWindowRef, NativeWindowService } from './shared/window.service';
import { MetadataService } from './core/metadata/metadata.service';
import { TranslateService } from '@ngx-translate/core';
import { GLOBAL_CONFIG, GlobalConfig } from '../config';
import { TransferState } from '../modules/transfer-state/transfer-state';
import { MetadataService } from './core/metadata/metadata.service';
import { HostWindowResizeAction } from './shared/host-window.actions';
import { HostWindowState } from './shared/host-window.reducer';
import { NativeWindowRef, NativeWindowService } from './shared/window.service';
@Component({
selector: 'ds-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
changeDetection: ChangeDetectionStrategy.Default,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit, OnChanges {
export class AppComponent implements OnInit {
constructor(
@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
@@ -71,9 +71,4 @@ export class AppComponent implements OnInit, OnChanges {
);
}
ngOnChanges(changes: SimpleChanges): void {
console.log('AppComponent: onchanges called', changes);
}
}

View File

@@ -1,6 +1,5 @@
import { Observable } from 'rxjs/Observable';
import { PageInfo } from '../shared/page-info.model';
import { hasValue } from '../../shared/empty.util';
export enum RemoteDataState {
RequestPending = 'RequestPending',
@@ -26,13 +25,13 @@ export class RemoteData<T> {
}
get state(): RemoteDataState {
if (this.isSuccessFul === true) {
if (this.isSuccessFul === true && hasValue(this.payload)) {
return RemoteDataState.Success
} else if (this.isSuccessFul === false) {
return RemoteDataState.Failed
} else if (this.requestPending === true) {
return RemoteDataState.RequestPending
} else if (this.responsePending === true || this.isSuccessFul === undefined) {
} else {
return RemoteDataState.ResponsePending
}
}

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { MemoizedSelector, Store } from '@ngrx/store';
import { createSelector, MemoizedSelector, Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { hasValue } from '../../shared/empty.util';
@@ -9,17 +9,24 @@ import { ObjectCacheService } from '../cache/object-cache.service';
import { DSOSuccessResponse, RestResponse } from '../cache/response-cache.models';
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
import { ResponseCacheService } from '../cache/response-cache.service';
import { CoreState } from '../core.reducers';
import { coreSelector, CoreState } from '../core.reducers';
import { keySelector } from '../shared/selectors';
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
import { RestRequest } from './request.models';
import { RequestEntry } from './request.reducer';
import { RequestEntry, RequestState } from './request.reducer';
function entryFromHrefSelector(href: string): MemoizedSelector<CoreState, RequestEntry> {
return keySelector<RequestEntry>('data/request', href);
}
export function requestStateSelector(): MemoizedSelector<CoreState, RequestState> {
return createSelector(coreSelector, (state: CoreState) => {
return state['data/request'] as RequestState;
});
}
@Injectable()
export class RequestService {
private requestsOnTheirWayToTheStore: string[] = [];
@@ -54,7 +61,6 @@ export class RequestService {
configure<T extends CacheableObject>(request: RestRequest): void {
let isCached = this.objectCache.hasBySelfLink(request.href);
// console.log('request.href', request.href);
if (!isCached && this.responseCache.has(request.href)) {
const [successResponse, errorResponse] = this.responseCache.get(request.href)
.take(1)

View File

@@ -17,7 +17,6 @@ export abstract class HALEndpointService {
const request = new RootEndpointRequest(this.EnvConfig);
this.requestService.configure(request);
return this.responseCache.get(request.href)
// .do((entry: ResponseCacheEntry) => console.log('entry.response', entry.response))
.map((entry: ResponseCacheEntry) => entry.response)
.filter((response: RootSuccessResponse) => isNotEmpty(response) && isNotEmpty(response.endpointMap))
.map((response: RootSuccessResponse) => response.endpointMap)

View File

@@ -36,7 +36,15 @@ export function boot(cache: TransferState, appRef: ApplicationRef, store: Store<
// authentication mechanism goes here
return () => {
appRef.isStable.filter((stable: boolean) => stable).first().subscribe(() => {
// isStable == true doesn't guarantee that all dispatched actions have been
// processed yet. So in those cases the store snapshot wouldn't be complete
// and a rehydrate would leave the app in a broken state
//
// This setTimeout without delay schedules the cache.inject() to happen ASAP
// after everything that's already scheduled, and it solves that problem.
setTimeout(() => {
cache.inject();
}, 0);
});
};
}