1 import {Component, OnInit, AfterViewInit, Renderer2} from '@angular/core';
2 import {ActivatedRoute} from '@angular/router';
3 import {IdlObject} from '@eg/core/idl.service';
4 import {OrgService} from '@eg/core/org.service';
5 import {CatalogService} from '@eg/share/catalog/catalog.service';
6 import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
7 import {StaffCatalogService} from './catalog.service';
8 import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
11 selector: 'eg-catalog-search-form',
12 styleUrls: ['search-form.component.css'],
13 templateUrl: 'search-form.component.html'
15 export class SearchFormComponent implements OnInit, AfterViewInit {
17 context: CatalogSearchContext;
18 ccvmMap: {[ccvm: string]: IdlObject[]} = {};
19 cmfMap: {[cmf: string]: IdlObject} = {};
20 showSearchFilters = false;
21 copyLocations: IdlObject[];
25 private renderer: Renderer2,
26 private route: ActivatedRoute,
27 private org: OrgService,
28 private cat: CatalogService,
29 private staffCat: StaffCatalogService
31 this.copyLocations = [];
33 // Some search scenarios, like rendering a search template,
34 // will not be searchable and thus not resovle to a specific
35 // search tab. Check to see if a specific tab is requested
37 this.route.queryParams.subscribe(params => {
38 if (params.searchTab) {
39 this.searchTab = params.searchTab;
45 this.ccvmMap = this.cat.ccvmMap;
46 this.cmfMap = this.cat.cmfMap;
47 this.context = this.staffCat.searchContext;
49 // Start with advanced search options open
50 // if any filters are active.
51 this.showSearchFilters = this.filtersActive();
55 // Query inputs are generated from search context data,
56 // so they are not available until after the first render.
57 // Search context data is extracted synchronously from the URL.
59 // Avoid changing the tab in the lifecycle hook thread.
62 if (this.context.identSearch.queryType === '') {
63 this.context.identSearch.queryType = 'identifier|isbn';
66 // Apply a tab if none was already specified
67 if (!this.searchTab) {
68 // Assumes that only one type of search will be searchable
70 if (this.context.marcSearch.isSearchable()) {
71 this.searchTab = 'marc';
72 } else if (this.context.identSearch.isSearchable()) {
73 this.searchTab = 'ident';
74 } else if (this.context.browseSearch.isSearchable()) {
75 this.searchTab = 'browse';
78 this.searchTab = 'term';
79 this.refreshCopyLocations();
87 onTabChange(evt: NgbTabChangeEvent) {
88 this.searchTab = evt.nextId;
90 // Focus after tab-change event has a chance to complete
91 // or the tab body and its input won't exist yet and no
92 // elements will be focus-able.
93 setTimeout(() => this.focusTabInput());
97 // Select a DOM node to focus when the tab changes.
99 switch (this.searchTab) {
101 selector = '#ident-query-input';
104 selector = '#first-marc-tag';
107 selector = '#browse-term-input';
110 selector = '#cnbrowse-term-input';
113 this.refreshCopyLocations();
114 selector = '#first-query-input';
118 // TODO: sometime the selector is not available in the DOM
119 // until even later (even with setTimeouts). Need to fix this.
120 // Note the error is thrown from selectRootElement(), not the
121 // call to .focus() on a null reference.
122 this.renderer.selectRootElement(selector).focus();
127 * Display the advanced/extended search options when asked to
128 * or if any advanced options are selected.
130 showFilters(): boolean {
131 // Note that filters may become active due to external
132 // actions on the search context. Always show the filters
133 // if filter values are applied.
134 return this.showSearchFilters || this.filtersActive();
138 this.showSearchFilters = !this.showSearchFilters;
139 this.refreshCopyLocations();
142 filtersActive(): boolean {
144 if (this.context.termSearch.copyLocations[0] !== '') { return true; }
146 // ccvm filters may be present without any filters applied.
147 // e.g. if filters were applied then removed.
149 Object.keys(this.context.termSearch.ccvmFilters).forEach(ccvm => {
150 if (this.context.termSearch.ccvmFilters[ccvm][0] !== '') {
158 orgOnChange = (org: IdlObject): void => {
159 this.context.searchOrg = org;
160 this.refreshCopyLocations();
163 refreshCopyLocations() {
164 if (!this.showFilters()) { return; }
166 // TODO: is this how we avoid displaying too many locations?
167 const org = this.context.searchOrg;
168 if (org.id() === this.org.root().id()) {
169 this.copyLocations = [];
173 this.cat.fetchCopyLocations(org).then(() =>
174 this.copyLocations = this.cat.copyLocations
178 orgName(orgId: number): string {
179 return this.org.get(orgId).shortname();
182 addSearchRow(index: number): void {
183 this.context.termSearch.query.splice(index, 0, '');
184 this.context.termSearch.fieldClass.splice(index, 0, 'keyword');
185 this.context.termSearch.joinOp.splice(index, 0, '&&');
186 this.context.termSearch.matchOp.splice(index, 0, 'contains');
189 delSearchRow(index: number): void {
190 this.context.termSearch.query.splice(index, 1);
191 this.context.termSearch.fieldClass.splice(index, 1);
192 this.context.termSearch.joinOp.splice(index, 1);
193 this.context.termSearch.matchOp.splice(index, 1);
196 addMarcSearchRow(index: number): void {
197 this.context.marcSearch.tags.splice(index, 0, '');
198 this.context.marcSearch.subfields.splice(index, 0, '');
199 this.context.marcSearch.values.splice(index, 0, '');
202 delMarcSearchRow(index: number): void {
203 this.context.marcSearch.tags.splice(index, 1);
204 this.context.marcSearch.subfields.splice(index, 1);
205 this.context.marcSearch.values.splice(index, 1);
208 searchByForm(): void {
209 this.context.pager.offset = 0; // New search
211 // Form search overrides basket display
212 this.context.showBasket = false;
214 this.context.scrub(this.searchTab);
216 switch (this.searchTab) {
221 this.staffCat.search();
225 this.staffCat.browse();
229 this.staffCat.cnBrowse();
234 // https://stackoverflow.com/questions/42322968/angular2-dynamic-input-field-lose-focus-when-input-changes
235 trackByIdx(index: any, item: any) {
239 searchIsActive(): boolean {
240 return this.context.searchState === CatalogSearchState.SEARCHING;