]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts
LP1615805 No inputs after submit in patron search (AngularJS)
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / catalog / result / results.component.ts
1 import {Component, OnInit, OnDestroy, Input} from '@angular/core';
2 import {Observable, Subscription} from 'rxjs';
3 import {tap, map, switchMap, distinctUntilChanged} from 'rxjs/operators';
4 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
5 import {CatalogService} from '@eg/share/catalog/catalog.service';
6 import {BibRecordService} from '@eg/share/catalog/bib-record.service';
7 import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service';
8 import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
9 import {PcrudService} from '@eg/core/pcrud.service';
10 import {StaffCatalogService} from '../catalog.service';
11 import {IdlObject} from '@eg/core/idl.service';
12 import {BasketService} from '@eg/share/catalog/basket.service';
13 import {ServerStoreService} from '@eg/core/server-store.service';
14
15 @Component({
16     selector: 'eg-catalog-results',
17     templateUrl: 'results.component.html',
18     styleUrls: ['results.component.css']
19 })
20 export class ResultsComponent implements OnInit, OnDestroy {
21
22     searchContext: CatalogSearchContext;
23
24     // Cache record creator/editor since this will likely be a
25     // reasonably small set of data w/ lots of repitition.
26     userCache: {[id: number]: IdlObject} = {};
27
28     allRecsSelected: boolean;
29
30     searchSub: Subscription;
31     routeSub: Subscription;
32     basketSub: Subscription;
33     showMoreDetails = false;
34
35     constructor(
36         private route: ActivatedRoute,
37         private pcrud: PcrudService,
38         private cat: CatalogService,
39         private bib: BibRecordService,
40         private catUrl: CatalogUrlService,
41         private staffCat: StaffCatalogService,
42         private serverStore: ServerStoreService,
43         private basket: BasketService,
44         private router: Router
45     ) {}
46
47     ngOnInit() {
48         this.searchContext = this.staffCat.searchContext;
49         this.staffCat.browsePagerData = [];
50
51         // Our search context is initialized on page load.  Once
52         // ResultsComponent is active, it will not be reinitialized,
53         // even if the route parameters changes (unless we change the
54         // route reuse policy).  Watch for changes here to pick up new
55         // searches.
56         //
57         // This will also fire on page load.
58         this.routeSub =
59             this.route.queryParamMap.subscribe((params: ParamMap) => {
60
61                 // TODO: Angular docs suggest using switchMap(), but
62                 // it's not firing for some reason.  Also, could avoid
63                 // firing unnecessary searches when a param unrelated to
64                 // searching is changed by .map()'ing out only the desired
65                 // params and running through .distinctUntilChanged(), but
66                 // .map() is not firing either.  I'm missing something.
67                 this.searchByUrl(params);
68             });
69
70         // After each completed search, update the record selector.
71         this.searchSub = this.cat.onSearchComplete.subscribe(
72             ctx => {
73                 this.jumpIfNecessary();
74                 this.applyRecordSelection();
75             }
76         );
77
78         // Watch for basket changes applied by other components.
79         this.basketSub = this.basket.onChange.subscribe(
80             () => this.applyRecordSelection());
81     }
82
83     ngOnDestroy() {
84         if (this.routeSub) {
85             this.routeSub.unsubscribe();
86             this.searchSub.unsubscribe();
87             this.basketSub.unsubscribe();
88         }
89     }
90
91     // For non-metarecord searches, jump to record page if only a
92     // single hit is returned and the jump is enabled by library setting.
93     // Unlike the OPAC version of jump-on-single-hit, the staff version
94     // does not attempt to jump to the bib if it is the single member
95     // of a sole metarecord returned by a metarecord search.
96     jumpIfNecessary() {
97         const ids = this.searchContext.currentResultIds();
98         if (
99             this.staffCat.jumpOnSingleHit &&
100             ids.length === 1 &&
101             !this.searchContext.termSearch.isMetarecordSearch()
102         ) {
103             this.router.navigate(['/staff/catalog/record/' + ids[0]], {queryParamsHandling: 'merge'});
104         }
105     }
106
107     // Apply the select-all checkbox when all visible records
108     // are selected.
109     applyRecordSelection() {
110         const ids = this.searchContext.currentResultIds();
111         let allChecked = true;
112         ids.forEach(id => {
113             if (!this.basket.hasRecordId(id)) {
114                 allChecked = false;
115             }
116         });
117         this.allRecsSelected = allChecked;
118     }
119
120     // Pull values from the URL and run the requested search.
121     searchByUrl(params: ParamMap): void {
122         this.catUrl.applyUrlParams(this.searchContext, params);
123
124
125         if (this.searchContext.isSearchable()) {
126
127             this.serverStore.getItem('eg.staff.catalog.results.show_more')
128                 .then(showMore => {
129
130                     this.showMoreDetails =
131                     this.searchContext.showResultExtras = showMore;
132
133                     if (this.staffCat.prefOrg) {
134                         this.searchContext.prefOu = this.staffCat.prefOrg.id();
135                     }
136
137                     this.cat.search(this.searchContext)
138                         .then(ok => {
139                             this.cat.fetchFacets(this.searchContext);
140                             this.cat.fetchBibSummaries(this.searchContext);
141                         });
142                 });
143         }
144     }
145
146     toggleShowMore() {
147         this.showMoreDetails = !this.showMoreDetails;
148
149         this.serverStore.setItem(
150             'eg.staff.catalog.results.show_more', this.showMoreDetails)
151             .then(_ => {
152
153                 this.searchContext.showResultExtras = this.showMoreDetails;
154
155                 if (this.showMoreDetails) {
156                     this.staffCat.search();
157                 } else {
158                 // Clear the collected copies.  No need for another search.
159                     this.searchContext.result.records.forEach(rec => rec.copies = undefined);
160                 }
161             });
162     }
163
164     searchIsDone(): boolean {
165         return this.searchContext.searchState === CatalogSearchState.COMPLETE;
166     }
167
168     searchIsActive(): boolean {
169         return this.searchContext.searchState === CatalogSearchState.SEARCHING;
170     }
171
172     searchHasResults(): boolean {
173         return this.searchIsDone() && this.searchContext.result.count > 0;
174     }
175
176     toggleAllRecsSelected() {
177         const ids = this.searchContext.currentResultIds();
178
179         if (this.allRecsSelected) {
180             this.basket.addRecordIds(ids);
181         } else {
182             this.basket.removeRecordIds(ids);
183         }
184     }
185 }
186
187