1 import {Component, OnInit, Input, ViewChild, HostListener} from '@angular/core';
2 import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
3 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
4 import {PcrudService} from '@eg/core/pcrud.service';
5 import {IdlObject} from '@eg/core/idl.service';
6 import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
7 import {CatalogService} from '@eg/share/catalog/catalog.service';
8 import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.service';
9 import {StaffCatalogService} from '../catalog.service';
10 import {BibSummaryComponent} from '@eg/staff/share/bib-summary/bib-summary.component';
11 import {StoreService} from '@eg/core/store.service';
12 import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
13 import {MarcEditorComponent} from '@eg/staff/share/marc-edit/editor.component';
16 selector: 'eg-catalog-record',
17 templateUrl: 'record.component.html'
19 export class RecordComponent implements OnInit {
23 summary: BibRecordSummary;
24 searchContext: CatalogSearchContext;
25 @ViewChild('recordTabs', { static: true }) recordTabs: NgbTabset;
26 @ViewChild('marcEditor', {static: false}) marcEditor: MarcEditorComponent;
27 defaultTab: string; // eg.cat.default_record_tab
29 @ViewChild('pendingChangesDialog', {static: false})
30 pendingChangesDialog: ConfirmDialogComponent;
33 private router: Router,
34 private route: ActivatedRoute,
35 private pcrud: PcrudService,
36 private bib: BibRecordService,
37 private cat: CatalogService,
38 private staffCat: StaffCatalogService,
39 private store: StoreService
43 this.searchContext = this.staffCat.searchContext;
46 this.store.getLocalItem('eg.cat.default_record_tab')
49 // Watch for URL record ID changes
50 // This includes the initial route.
51 // When applying the default configured tab, no navigation occurs
52 // to apply the tab name to the URL, it displays as the default.
53 // This is done so no intermediate redirect is required, which
54 // messes with browser back/forward navigation.
55 this.route.paramMap.subscribe((params: ParamMap) => {
56 this.recordTab = params.get('tab');
57 this.recordId = +params.get('id');
58 this.searchContext = this.staffCat.searchContext;
60 if (!this.recordTab) {
61 this.recordTab = this.defaultTab || 'catalog';
69 this.defaultTab = this.recordTab;
70 this.store.setLocalItem('eg.cat.default_record_tab', this.recordTab);
73 // Changing a tab in the UI means changing the route.
74 // Changing the route ultimately results in changing the tab.
75 beforeTabChange(evt: NgbTabChangeEvent) {
77 // prevent tab changing until after route navigation
80 // Protect against tab changes with dirty data.
81 this.canDeactivate().then(ok => {
83 this.recordTab = evt.nextId;
90 * Handle 3 types of navigation which can cause loss of data.
91 * 1. Record detail tab navigation (see also beforeTabChange())
92 * 2. Intra-Angular route navigation away from the record detail page
93 * 3. Browser page unload/reload
95 * For the #1, and #2, display a eg confirmation dialog.
96 * For #3 use the stock browser onbeforeunload dialog.
98 * Note in this case a tab change is a route change, but it's one
99 * which does not cause RecordComponent to unload, so it has to be
100 * manually tracked in beforeTabChange().
102 @HostListener('window:beforeunload', ['$event'])
103 canDeactivate($event?: Event): Promise<boolean> {
105 if (this.marcEditor && this.marcEditor.changesPending()) {
107 // Each warning dialog clears the current "changes are pending"
108 // flag so the user is not presented with the dialog again
109 // unless new changes are made.
110 this.marcEditor.clearPendingChanges();
112 if ($event) { // window.onbeforeunload
113 $event.preventDefault();
114 $event.returnValue = true;
116 } else { // tab OR route change.
117 return this.pendingChangesDialog.open().toPromise();
121 return Promise.resolve(true);
127 `/staff/catalog/record/${this.recordId}/${this.recordTab}`;
129 // Retain search parameters
130 this.router.navigate([url], {queryParamsHandling: 'merge'});
135 // Avoid re-fetching the same record summary during tab navigation.
136 if (this.staffCat.currentDetailRecordSummary &&
137 this.recordId === this.staffCat.currentDetailRecordSummary.id) {
138 this.summary = this.staffCat.currentDetailRecordSummary;
143 this.bib.getBibSummary(
145 this.searchContext.searchOrg.id(),
146 this.searchContext.searchOrg.ou_type().depth()).toPromise()
149 this.staffCat.currentDetailRecordSummary = summary;
150 this.bib.fleshBibUsers([summary.record]);
154 currentSearchOrg(): IdlObject {
155 if (this.staffCat && this.staffCat.searchContext) {
156 return this.staffCat.searchContext.searchOrg;
161 handleMarcRecordSaved() {
162 this.staffCat.currentDetailRecordSummary = null;