1 import {Component, Input, Output, OnInit, AfterViewInit, EventEmitter,
2 ViewChild, OnDestroy} from '@angular/core';
3 import {filter} from 'rxjs/operators';
4 import {IdlService} from '@eg/core/idl.service';
5 import {NetService} from '@eg/core/net.service';
6 import {OrgService} from '@eg/core/org.service';
7 import {ServerStoreService} from '@eg/core/server-store.service';
8 import {TagTableService} from './tagtable.service';
9 import {MarcRecord, MarcField} from './marcrecord';
10 import {MarcEditContext} from './editor-context';
11 import {AuthorityLinkingDialogComponent} from './authority-linking-dialog.component';
15 * MARC Record rich editor interface.
19 selector: 'eg-marc-rich-editor',
20 templateUrl: './rich-editor.component.html',
21 styleUrls: ['rich-editor.component.css']
24 export class MarcRichEditorComponent implements OnInit {
26 @Input() context: MarcEditContext;
27 get record(): MarcRecord { return this.context.record; }
31 randId = Math.floor(Math.random() * 100000);
32 stackSubfields: boolean;
33 controlledBibTags: string[] = [];
35 @ViewChild('authLinker', {static: false})
36 authLinker: AuthorityLinkingDialogComponent;
39 private idl: IdlService,
40 private net: NetService,
41 private org: OrgService,
42 private store: ServerStoreService,
43 private tagTable: TagTableService
48 this.store.getItem('cat.marcedit.stack_subfields')
49 .then(stack => this.stackSubfields = stack);
52 this.context.recordChange.subscribe(__ => this.init()));
54 // Changing the Type fixed field means loading new meta-metadata.
55 this.record.fixedFieldChange.pipe(filter(code => code === 'Type'))
56 .subscribe(_ => this.init());
59 init(): Promise<any> {
60 this.dataLoaded = false;
62 if (!this.record) { return Promise.resolve(); }
65 this.tagTable.loadTagTable({marcRecordType: this.context.recordType}),
66 this.tagTable.getFfPosTable(this.record.recordType()),
67 this.tagTable.getFfValueTable(this.record.recordType()),
68 this.tagTable.getControlledBibTags().then(
69 tags => this.controlledBibTags = tags)
71 // setTimeout forces all of our sub-components to rerender
72 // themselves each time init() is called. Without this,
73 // changing the record Type would only re-render the fixed
74 // fields editor when data had to be fetched from the
75 // network. (Sometimes the data is cached).
76 setTimeout(() => this.dataLoaded = true)
80 stackSubfieldsChange() {
81 if (this.stackSubfields) {
82 this.store.setItem('cat.marcedit.stack_subfields', true);
84 this.store.removeItem('cat.marcedit.stack_subfields');
89 return this.context.undoCount();
93 return this.context.redoCount();
97 this.context.requestUndo();
101 this.context.requestRedo();
104 controlFields(): MarcField[] {
105 return this.record.fields.filter(f => f.isCtrlField);
108 dataFields(): MarcField[] {
109 return this.record.fields.filter(f => !f.isCtrlField);
114 this.record.fields.filter(f => this.isControlledBibTag(f.tag))
119 id: f.fieldId, // ignored and echoed by server
123 subfields: f.subfields.map(sf => ({code: sf[0], value: sf[1]}))
127 this.net.request('open-ils.cat',
128 'open-ils.cat.authority.validate.bib_field', fields)
129 .subscribe(checkedField => {
130 const bibField = this.record.fields
131 .filter(f => f.fieldId === +checkedField.id)[0];
133 bibField.authChecked = true;
134 bibField.authValid = checkedField.valid;
138 isControlledBibTag(tag: string): boolean {
139 return this.controlledBibTags && this.controlledBibTags.includes(tag);
142 openLinkerDialog(field: MarcField) {
143 this.authLinker.bibField = field;
144 this.authLinker.open({size: 'lg'}).subscribe(newField => {
145 if (!newField) { return; }
147 // Performs an insert followed by a delete, so the two
148 // fields can be tracked separately for undo/redo actions.
149 const marcField = this.record.newField(newField);
150 this.context.insertField(field, marcField);
151 this.context.deleteField(field);
153 // Mark the insert and delete as an atomic undo/redo action.
154 this.context.setUndoGroupSize(2);