-import {Injectable} from '@angular/core';
+import {Injectable, EventEmitter} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {StoreService} from '@eg/core/store.service';
import {NetService} from '@eg/core/net.service';
idList: number[];
+ // Fired every time our list of ID's are updated.
+ onChange: EventEmitter<number[]>;
+
constructor(
private net: NetService,
private pcrud: PcrudService,
private store: StoreService,
private anonCache: AnonCacheService
- ) { this.idList = []; }
+ ) {
+ this.idList = [];
+ this.onChange = new EventEmitter<number[]>();
+ }
hasRecordId(id: number): boolean {
return this.idList.indexOf(Number(id)) > -1;
return this.anonCache.setItem(cacheKey, BASKET_CACHE_ATTR, this.idList)
.then(cacheKey => {
this.store.setLoginSessionItem(BASKET_CACHE_KEY_COOKIE, cacheKey);
+ this.onChange.emit(this.idList);
return this.idList;
});
}
addRecordIds(ids: number[]): Promise<number[]> {
ids = ids.filter(id => !this.hasRecordId(id)); // avoid dupes
+
+ if (ids.length === 0) {
+ return Promise.resolve(this.idList);
+ }
return this.setRecordIds(
this.idList.concat(ids.map(id => Number(id))));
}
removeRecordIds(ids: number[]): Promise<number[]> {
+
+ if (this.idList.length === 0) {
+ return Promise.resolve(this.idList);
+ }
+
const wantedIds = this.idList.filter(
id => ids.indexOf(Number(id)) < 0);
- return this.setRecordIds(wantedIds);
+
+ return this.setRecordIds(wantedIds); // OK if empty
}
}
-import {Injectable} from '@angular/core';
+import {Injectable, EventEmitter} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {mergeMap} from 'rxjs/operators/mergeMap';
import {map} from 'rxjs/operators/map';
lastFacetData: any;
lastFacetKey: string;
+ // Allow anyone to watch for completed searches.
+ onSearchComplete: EventEmitter<CatalogSearchContext>;
+
constructor(
private idl: IdlService,
private net: NetService,
private unapi: UnapiService,
private pcrud: PcrudService,
private bibService: BibRecordService
- ) {}
+ ) {
+ this.onSearchComplete = new EventEmitter<CatalogSearchContext>();
+
+ }
search(ctx: CatalogSearchContext): Promise<void> {
ctx.searchState = CatalogSearchState.SEARCHING;
).subscribe(result => {
this.applyResultData(ctx, result);
ctx.searchState = CatalogSearchState.COMPLETE;
+ this.onSearchComplete.emit(ctx);
resolve();
});
});
<span class="font-weight-bold font-italic">
{{index + 1 + searchContext.pager.offset}}.
</span>
- <input class="pl-1" type='checkbox' [(ngModel)]="isRecordInBasket"
+ <input class="pl-1" type='checkbox' [(ngModel)]="isRecordSelected"
(change)="toggleBasketEntry()"/>
</label>
<!-- XXX hard-coded width so columns align vertically regardless
-import {Component, OnInit, Input} from '@angular/core';
+import {Component, OnInit, OnDestroy, Input} from '@angular/core';
+import {Subscription} from 'rxjs/Subscription';
import {Router} from '@angular/router';
import {OrgService} from '@eg/core/org.service';
import {NetService} from '@eg/core/net.service';
templateUrl: 'record.component.html',
styleUrls: ['record.component.css']
})
-export class ResultRecordComponent implements OnInit {
+export class ResultRecordComponent implements OnInit, OnDestroy {
@Input() index: number; // 0-index display row
@Input() summary: BibRecordSummary;
searchContext: CatalogSearchContext;
- isRecordInBasket: boolean;
+ isRecordSelected: boolean;
+ basketSub: Subscription;
constructor(
private router: Router,
ngOnInit() {
this.searchContext = this.staffCat.searchContext;
this.summary.getHoldCount();
- this.isRecordInBasket = this.basket.hasRecordId(this.summary.id);
+ this.isRecordSelected = this.basket.hasRecordId(this.summary.id);
+
+ // Watch for basket changes caused by other components
+ this.basketSub = this.basket.onChange.subscribe(() => {
+ this.isRecordSelected = this.basket.hasRecordId(this.summary.id);
+ });
+ }
+
+ ngOnDestroy() {
+ this.basketSub.unsubscribe();
}
orgName(orgId: number): string {
}
toggleBasketEntry() {
- if (this.isRecordInBasket) {
+ if (this.isRecordSelected) {
return this.basket.addRecordIds([this.summary.id]);
} else {
return this.basket.removeRecordIds([this.summary.id]);
</div>
<div class="col-lg-2">
<label class="checkbox">
- <input type='checkbox'/>
+ <input type='checkbox' [(ngModel)]="allRecsSelected"
+ (change)="toggleAllRecsSelected()"/>
<span class="pl-1" i18n>Select {{searchContext.pager.rowNumber(0)}} -
{{searchContext.pager.rowNumber(searchContext.pager.limit - 1)}}
</span>
-import {Component, OnInit, Input} from '@angular/core';
+import {Component, OnInit, OnDestroy, Input} from '@angular/core';
import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
import {map, switchMap, distinctUntilChanged} from 'rxjs/operators';
import {ActivatedRoute, ParamMap} from '@angular/router';
import {CatalogService} from '@eg/share/catalog/catalog.service';
import {PcrudService} from '@eg/core/pcrud.service';
import {StaffCatalogService} from '../catalog.service';
import {IdlObject} from '@eg/core/idl.service';
+import {BasketService} from '@eg/share/catalog/basket.service';
@Component({
selector: 'eg-catalog-results',
templateUrl: 'results.component.html'
})
-export class ResultsComponent implements OnInit {
+export class ResultsComponent implements OnInit, OnDestroy {
searchContext: CatalogSearchContext;
// reasonably small set of data w/ lots of repitition.
userCache: {[id: number]: IdlObject} = {};
+ allRecsSelected: boolean;
+
+ searchSub: Subscription;
+ routeSub: Subscription;
+
constructor(
private route: ActivatedRoute,
private pcrud: PcrudService,
private cat: CatalogService,
private bib: BibRecordService,
private catUrl: CatalogUrlService,
- private staffCat: StaffCatalogService
+ private staffCat: StaffCatalogService,
+ private basket: BasketService
) {}
ngOnInit() {
// searches.
//
// This will also fire on page load.
- this.route.queryParamMap.subscribe((params: ParamMap) => {
+ this.routeSub =
+ this.route.queryParamMap.subscribe((params: ParamMap) => {
// TODO: Angular docs suggest using switchMap(), but
// it's not firing for some reason. Also, could avoid
// .map() is not firing either. I'm missing something.
this.searchByUrl(params);
});
+
+ // After each completed search, update the record selector.
+ this.searchSub = this.cat.onSearchComplete.subscribe(
+ ctx => this.applyRecordSelection());
+ }
+
+ ngOnDestroy() {
+ this.routeSub.unsubscribe();
+ this.searchSub.unsubscribe();
+ }
+
+ // Apply the select-all checkbox when all visible records
+ // are selected.
+ applyRecordSelection() {
+ const ids = this.searchContext.currentResultIds();
+ let allChecked = true;
+ ids.forEach(id => {
+ if (!this.basket.hasRecordId(id)) {
+ allChecked = false;
+ }
+ });
+ this.allRecsSelected = allChecked;
}
searchByUrl(params: ParamMap): void {
}
}
- // Avoid starting to display records until the first few are ready
- // to reduce page shuffling.
+ // Records file into place randomly as the server returns data.
+ // To reduce page display shuffling, avoid showing the list of
+ // records until the first few are ready to render.
shouldStartRendering(): boolean {
- return (
- this.searchContext.result &&
- this.searchContext.result.records && (
- this.searchContext.result.records.length === 0 ||
- this.searchContext.result.records[0]
- )
- );
+
+ if (this.searchHasResults()) {
+ const pageCount = this.searchContext.currentResultIds().length;
+ switch (pageCount) {
+ case 1:
+ return this.searchContext.result.records[0];
+ case 2:
+ return this.searchContext.result.records[0]
+ && this.searchContext.result.records[1];
+ default:
+ return this.searchContext.result.records[0]
+ && this.searchContext.result.records[1]
+ && this.searchContext.result.records[2];
+ }
+ }
+
+ return false;
}
fleshSearchResults(): void {
searchHasResults(): boolean {
return this.searchIsDone() && this.searchContext.result.count > 0;
}
+
+ toggleAllRecsSelected() {
+ const ids = this.searchContext.currentResultIds();
+
+ if (this.allRecsSelected) {
+ this.basket.addRecordIds(ids);
+ } else {
+ this.basket.removeRecordIds(ids);
+ }
+ }
}