var owner_name_list = [];
while (owner.parent_ou()) { // we're going to skip the top of the tree...
- owner_name_list.unshift(owner.name());
+ owner_name_list.unshift(owner.shortname());
owner = egCore.org.get(owner.parent_ou());
}
"@types/jasminewd2": "~2.0.2",
"@types/jquery": "^3.2.16",
"@types/node": "~6.0.60",
+ "@types/xml2js": "^0.4.2",
"codelyzer": "~3.2.0",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
import {Router} from '@angular/router'; // Debugging
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import {CookieModule} from 'ngx-cookie'; // import CookieMonster
+import {HttpClientModule} from '@angular/common/http';
import {EgBaseComponent} from './base.component';
import {EgBaseRoutingModule} from './routing.module';
import {EgEventService} from '@eg/core/event';
import {EgStoreService} from '@eg/core/store';
import {EgIdlService} from '@eg/core/idl';
-import {EgNetService} from '@eg/core/net.service';
+import {EgNetService} from '@eg/core/net';
import {EgAuthService} from '@eg/core/auth';
import {EgPcrudService} from '@eg/core/pcrud';
import {EgOrgService} from '@eg/core/org';
EgBaseRoutingModule,
BrowserModule,
NgbModule.forRoot(),
- CookieModule.forRoot()
+ CookieModule.forRoot(),
+ HttpClientModule
],
providers: [
EgEventService,
-Core types (classes) and Angular services used by all modules.
+Core Angular services and assocated types/classes.
-NOTES:
+Core services are imported and exported by the base module, which means
+they are automatically added as dependencies to ALL applications.
+
+1. Only add services here that are universally required!
+2. Avoid path navigation in the core services as paths will vary by application.
-* Avoid path navigation in the core services as paths will vary by application.
*/
import { Injectable, EventEmitter } from '@angular/core';
import { Observable } from 'rxjs/Rx';
-import { EgNetService } from './net.service';
+import { EgNetService } from './net';
import { EgEventService, EgEvent } from './event';
import { EgIdlService, EgIdlObject } from './idl';
import { EgStoreService } from './store';
+++ /dev/null
-/**
- *
- * constructor(private net : EgNetService) {
- * ...
- * egNet.request(service, method, param1 [, param2, ...])
- * .subscribe(
- * (res) => console.log('received one resopnse: ' + res),
- * (err) => console.error('recived request error: ' + err),
- * () => console.log('request complete')
- * )
- * );
- * ...
- * }
- *
- * Each response is relayed via Observable onNext(). The interface is
- * the same for streaming and atomic requests.
- */
-import { Injectable, EventEmitter } from '@angular/core';
-import { Observable, Observer } from 'rxjs/Rx';
-import { EgEventService, EgEvent } from './event';
-
-// Global vars from opensrf.js
-// These are availavble at runtime, but are not exported.
-declare var OpenSRF, OSRF_TRANSPORT_TYPE_WS;
-
-export class EgNetRequest {
- service : String;
- method : String;
- params : any[];
- observer : Observer<any>;
- superseded : Boolean = false;
- // If set, this will be used instead of a one-off OpenSRF.ClientSession.
- session? : any;
-
- // Last EgEvent encountered by this request.
- // Most callers will not need to import EgEvent since the parsed
- // event will be available here.
- evt: EgEvent;
-
- constructor(service: String, method: String, params: any[], session?: any) {
- this.service = service;
- this.method = method;
- this.params = params;
- if (session) {
- this.session = session;
- } else {
- this.session = new OpenSRF.ClientSession(service);
- }
- }
-}
-
-@Injectable()
-export class EgNetService {
-
- permFailed$: EventEmitter<EgNetRequest>;
- authExpired$: EventEmitter<EgNetRequest>;
-
- // If true, permission failures are emitted via permFailed$
- // and the active request is marked as superseded.
- permFailedHasHandler: Boolean = false;
-
- constructor(
- private egEvt: EgEventService
- ) {
- this.permFailed$ = new EventEmitter<EgNetRequest>();
- this.authExpired$ = new EventEmitter<EgNetRequest>();
- }
-
- // Standard request call -- Variadic params version
- request(service: String, method: String, ...params: any[]): Observable<any> {
- return this.requestWithParamList(service, method, params);
- }
-
- // Array params version
- requestWithParamList(service: String,
- method: String, params: any[]): Observable<any> {
- return this.requestCompiled(
- new EgNetRequest(service, method, params));
- }
-
- requestCompiled(request: EgNetRequest): Observable<any> {
- return Observable.create(
- observer => {
- request.observer = observer;
- this.sendCompiledRequest(request);
- }
- );
- }
-
- // Version with pre-compiled EgNetRequest object
- sendCompiledRequest(request: EgNetRequest): void {
- OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS;
- var this_ = this;
-
- request.session.request({
- async : true,
- method : request.method,
- params : request.params,
- oncomplete : function() {
- // A superseded request will be complete()'ed by the
- // superseder at a later time.
- if (!request.superseded)
- request.observer.complete();
- },
- onresponse : function(r) {
- this_.dispatchResponse(request, r.recv().content());
- },
- onerror : function(errmsg) {
- let msg = `${request.method} failed! See server logs. ${errmsg}`;
- console.error(msg);
- request.observer.error(msg);
- },
- onmethoderror : function(req, statCode, statMsg) {
- let msg =
- `${request.method} failed! stat=${statCode} msg=${statMsg}`;
- console.error(msg);
-
- if (request.service == 'open-ils.pcrud' && statCode == 401) {
- // 401 is the PCRUD equivalent of a NO_SESSION event
- this_.authExpired$.emit(request);
- }
-
- request.observer.error(msg);
- }
-
- }).send();
- }
-
- // Relay response object to the caller for typical/successful responses.
- // Applies special handling to response events that require global attention.
- private dispatchResponse = function(request, response) {
- request.evt = this.egEvt.parse(response);
-
- if (request.evt) {
- switch(request.evt.textcode) {
-
- case 'NO_SESSION':
- console.debug(`EgNet emitting event: ${request.evt}`);
- request.observer.error(request.evt.toString());
- this.authExpired$.emit(request);
- return;
-
- case 'PERM_FAILURE':
- if (this.permFailedHasHandler) {
- console.debug(`EgNet emitting event: ${request.evt}`);
- request.superseded = true;
- this.permFailed$.emit(request);
- return;
- }
- }
- }
-
- // Pass the response to the caller.
- request.observer.next(response);
- };
-}
--- /dev/null
+/**
+ *
+ * constructor(private net : EgNetService) {
+ * ...
+ * egNet.request(service, method, param1 [, param2, ...])
+ * .subscribe(
+ * (res) => console.log('received one resopnse: ' + res),
+ * (err) => console.error('recived request error: ' + err),
+ * () => console.log('request complete')
+ * )
+ * );
+ * ...
+ * }
+ *
+ * Each response is relayed via Observable onNext(). The interface is
+ * the same for streaming and atomic requests.
+ */
+import { Injectable, EventEmitter } from '@angular/core';
+import { Observable, Observer } from 'rxjs/Rx';
+import { EgEventService, EgEvent } from './event';
+
+// Global vars from opensrf.js
+// These are availavble at runtime, but are not exported.
+declare var OpenSRF, OSRF_TRANSPORT_TYPE_WS;
+
+export class EgNetRequest {
+ service : String;
+ method : String;
+ params : any[];
+ observer : Observer<any>;
+ superseded : Boolean = false;
+ // If set, this will be used instead of a one-off OpenSRF.ClientSession.
+ session? : any;
+
+ // Last EgEvent encountered by this request.
+ // Most callers will not need to import EgEvent since the parsed
+ // event will be available here.
+ evt: EgEvent;
+
+ constructor(service: String, method: String, params: any[], session?: any) {
+ this.service = service;
+ this.method = method;
+ this.params = params;
+ if (session) {
+ this.session = session;
+ } else {
+ this.session = new OpenSRF.ClientSession(service);
+ }
+ }
+}
+
+@Injectable()
+export class EgNetService {
+
+ permFailed$: EventEmitter<EgNetRequest>;
+ authExpired$: EventEmitter<EgNetRequest>;
+
+ // If true, permission failures are emitted via permFailed$
+ // and the active request is marked as superseded.
+ permFailedHasHandler: Boolean = false;
+
+ constructor(
+ private egEvt: EgEventService
+ ) {
+ this.permFailed$ = new EventEmitter<EgNetRequest>();
+ this.authExpired$ = new EventEmitter<EgNetRequest>();
+ }
+
+ // Standard request call -- Variadic params version
+ request(service: String, method: String, ...params: any[]): Observable<any> {
+ return this.requestWithParamList(service, method, params);
+ }
+
+ // Array params version
+ requestWithParamList(service: String,
+ method: String, params: any[]): Observable<any> {
+ return this.requestCompiled(
+ new EgNetRequest(service, method, params));
+ }
+
+ requestCompiled(request: EgNetRequest): Observable<any> {
+ return Observable.create(
+ observer => {
+ request.observer = observer;
+ this.sendCompiledRequest(request);
+ }
+ );
+ }
+
+ // Version with pre-compiled EgNetRequest object
+ sendCompiledRequest(request: EgNetRequest): void {
+ OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS;
+ var this_ = this;
+
+ request.session.request({
+ async : true,
+ method : request.method,
+ params : request.params,
+ oncomplete : function() {
+ // A superseded request will be complete()'ed by the
+ // superseder at a later time.
+ if (!request.superseded)
+ request.observer.complete();
+ },
+ onresponse : function(r) {
+ this_.dispatchResponse(request, r.recv().content());
+ },
+ onerror : function(errmsg) {
+ let msg = `${request.method} failed! See server logs. ${errmsg}`;
+ console.error(msg);
+ request.observer.error(msg);
+ },
+ onmethoderror : function(req, statCode, statMsg) {
+ let msg =
+ `${request.method} failed! stat=${statCode} msg=${statMsg}`;
+ console.error(msg);
+
+ if (request.service == 'open-ils.pcrud' && statCode == 401) {
+ // 401 is the PCRUD equivalent of a NO_SESSION event
+ this_.authExpired$.emit(request);
+ }
+
+ request.observer.error(msg);
+ }
+
+ }).send();
+ }
+
+ // Relay response object to the caller for typical/successful responses.
+ // Applies special handling to response events that require global attention.
+ private dispatchResponse = function(request, response) {
+ request.evt = this.egEvt.parse(response);
+
+ if (request.evt) {
+ switch(request.evt.textcode) {
+
+ case 'NO_SESSION':
+ console.debug(`EgNet emitting event: ${request.evt}`);
+ request.observer.error(request.evt.toString());
+ this.authExpired$.emit(request);
+ return;
+
+ case 'PERM_FAILURE':
+ if (this.permFailedHasHandler) {
+ console.debug(`EgNet emitting event: ${request.evt}`);
+ request.superseded = true;
+ this.permFailed$.emit(request);
+ return;
+ }
+ }
+ }
+
+ // Pass the response to the caller.
+ request.observer.next(response);
+ };
+}
import {Observable, Observer} from 'rxjs/Rx';
//import {toPromise} from 'rxjs/operators';
import {EgIdlService, EgIdlObject} from './idl';
-import {EgNetService, EgNetRequest} from './net.service';
+import {EgNetService, EgNetRequest} from './net';
import {EgAuthService} from './auth';
// Used for debugging.
--- /dev/null
+Common Angular services and associated types/classes.
+
+This collection of services MIGHT be used by practically all applications.
+They are NOT automatically imported/exported by the base module and should
+be loaded within the requesting application as needed.
+
+
--- /dev/null
+import {Injectable, EventEmitter} from '@angular/core';
+import {EgOrgService} from '@eg/core/org';
+import {HttpClient} from '@angular/common/http';
+import {Parser} from 'xml2js';
+
+/*
+TODO: Add Display Fields to UNAPI
+https://library.biz/opac/extras/unapi?id=tag::U2@bre/1{bre.extern,holdings_xml,mra}/BR1/0&format=mods32
+*/
+
+const UNAPI_PATH = '/opac/extras/unapi?id=tag::U2@';
+
+interface EgUnapiParams {
+ target: string; // bre, ...
+ id: number | string; // 1 | 1,2,3,4,5
+ extras: string; // {holdings_xml,mra,...}
+ format: string; // mods32, marxml, ...
+ orgId?: number; // org unit ID
+ depth?: number; // org unit depth
+};
+
+@Injectable()
+export class EgUnapiService {
+
+ constructor(
+ private org: EgOrgService,
+ private http: HttpClient
+ ) {}
+
+ /**
+ * Retrieve an UNAPI document and return it as an XML string
+ */
+ getAsXmlString(params: EgUnapiParams): Promise<string> {
+ let depth = params.depth || 0;
+ let org = params.orgId ? this.org.get(params.orgId) : this.org.root();
+
+ let url = `${UNAPI_PATH}${params.target}/${params.id}${params.extras}/` +
+ `${org.shortname()}/${depth}&format=${params.format}`;
+
+ return new Promise((resolve, reject) => {
+ this.http.get(url, {responseType: 'text'})
+ .subscribe(xmlStr => resolve(xmlStr));
+ });
+ }
+
+ /**
+ * Retrieve an UNAPI document and return its object form, as
+ * generated by xml2js.
+ */
+ getAsObject(params: EgUnapiParams): Promise<any> {
+ return new Promise((resolve, reject) => {
+ this.getAsXmlString(params)
+ .then(xmlStr => {
+ new Parser().parseString(xmlStr, (err, unapiBlob) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(unapiBlob);
+ }
+ });
+ });
+ });
+ }
+}
+
+
import {ActivatedRoute} from '@angular/router';
import {EgStoreService} from '@eg/core/store';
import {EgIdlObject} from '@eg/core/idl';
-import {EgNetService} from '@eg/core/net.service';
+import {EgNetService} from '@eg/core/net';
import {EgAuthService} from '@eg/core/auth';
import {EgOrgService} from '@eg/core/org';
--- /dev/null
+
+<h1>CATALOG</h1>
+
+<router-outlet></router-outlet>
--- /dev/null
+import {Component, OnInit} from '@angular/core';
+import {Observable} from 'rxjs/Rx';
+import {ActivatedRoute} from '@angular/router';
+
+@Component({
+ templateUrl: 'catalog.component.html'
+})
+export class EgCatalogComponent implements OnInit {
+ constructor() {}
+
+ ngOnInit() { }
+
+}
+
--- /dev/null
+import {CommonModule} from '@angular/common';
+import {NgModule} from '@angular/core';
+import {EgStaffModule} from '../staff.module';
+import {EgCatalogRoutingModule} from './routing.module';
+import {EgUnapiService} from '@eg/share/unapi';
+import {EgCatalogComponent} from './catalog.component';
+import {EgCatalogSearchComponent} from './search.component';
+import {EgCatalogService} from './catalog.service';
+
+@NgModule({
+ declarations: [
+ EgCatalogComponent,
+ EgCatalogSearchComponent
+ ],
+ imports: [
+ EgStaffModule,
+ CommonModule,
+ EgCatalogRoutingModule
+ ],
+ providers: [
+ EgUnapiService,
+ EgCatalogService
+ ]
+})
+
+export class EgCatalogModule {
+
+}
--- /dev/null
+import {Injectable, EventEmitter} from '@angular/core';
+import {EgOrgService} from '@eg/core/org';
+import {EgUnapiService} from '@eg/share/unapi';
+import {EgIdlObject} from '@eg/core/idl';
+import {EgNetService} from '@eg/core/net';
+import {EgPcrudService} from '@eg/core/pcrud';
+
+const CCVM_FILTER_TYPES = [
+ 'item_type',
+ 'item_form',
+ 'item_lang',
+ 'audience',
+ 'audience_group',
+ 'vr_format',
+ 'bib_level',
+ 'lit_form',
+ 'search_format'
+];
+
+class Paginator {
+ offset: number = 0;
+ limit: number = 15;
+ resultCount: number;
+
+ isFirstPage(): boolean {
+ return this.offset == 0;
+ }
+
+ isLastPage(): boolean {
+ return this.currentPage() == this.pageCount();
+ }
+
+ currentPage(): number {
+ return Math.floor(this.offset / this.limit) + 1
+ }
+
+ pageCount(): number {
+ let pages = this.resultCount / this.limit;
+ if (Math.floor(pages) < pages)
+ pages = Math.floor(pages) + 1;
+ return pages;
+ }
+
+ pageList(): number[] {
+ let list = [];
+ for(let i = 1; i <= this.pageCount(); i++)
+ list.push(i);
+ return list;
+ }
+}
+
+export class FacetFilter {
+ facetClass: string;
+ facetName: string;
+ facetValue: string;
+}
+
+export class CatalogContext {
+ available: boolean = false;
+ global: boolean = false;
+ sort: string;
+ searchClass: string[];
+ query: string[];
+ joiner: string[];
+ match: string[];
+ format: string;
+ searchOrg: EgIdlObject;
+ ccvmFilters: {[ccvm:string] : string} = {};
+ facetFilters: FacetFilter[] = [];
+ paginator: Paginator = new Paginator();
+ result: any;
+ org: EgOrgService;
+
+ compileSearch(): string {
+
+ let str: string = '';
+
+ if (this.available) str += ' #available';
+
+ if (this.sort) {
+ // e.g. title, title.descending
+ let parts = this.sort.split(/\./);
+ if (parts[1]) str += ' #descending';
+ str += ' sort(' + parts[0] + ')';
+ }
+
+ // -------
+ // Compile boolean sub-query components
+ if (str.length) str += ' ';
+ let qcount = this.query.length;
+
+ // if we multiple boolean query components, wrap them in parens.
+ if (qcount > 1) str += '(';
+ this.query.forEach((q, idx) => {
+ str += this.compileBoolQuerySet(idx)
+ });
+ if (qcount > 1) str += ')';
+ // -------
+
+ if (this.format) {
+ str += ' format(' + this.format + ')';
+ }
+
+ if (this.global) {
+ str += ' depth(' +
+ this.org.root().ou_type().depth() + ')';
+ }
+
+ str += ' site(' + this.searchOrg.shortname() + ')';
+
+ Object.keys(this.ccvmFilters).forEach(field => {
+ str += ' ' + field + '(' + this.ccvmFilters[field] + ')';
+ });
+
+ this.facetFilters.forEach(f => {
+ str += ' ' + f.facetClass + '|'
+ + f.facetName + '[' + f.facetValue + ']';
+ });
+
+ return str;
+ }
+
+ stripQuotes(query: string): string {
+ return query.replace(/"/g, '');
+ }
+
+ stripAnchors(query: string): string {
+ return query.replace(/[\^\$]/g, '');
+ }
+
+ addQuotes(query: string): string {
+ if (query.match(/ /))
+ return '"' + query + '"'
+ return query;
+ };
+
+ compileBoolQuerySet(idx: number): string {
+ let query = this.query[idx];
+ let joiner = this.joiner[idx];
+ let match = this.match[idx];
+ let searchClass = this.searchClass[idx];
+
+ let str = '';
+ if (!query) return str;
+
+ if (idx > 0) str += ' ' + joiner + ' ';
+
+ str += '(';
+ if (searchClass) str += searchClass + ':';
+
+ switch(match) {
+ case 'phrase':
+ query = this.addQuotes(this.stripQuotes(query));
+ break;
+ case 'nocontains':
+ query = '-' + this.addQuotes(this.stripQuotes(query));
+ break;
+ case 'exact':
+ query = '^' + this.stripAnchors(query) + '$';
+ break;
+ case 'starts':
+ query = this.addQuotes('^' +
+ this.stripAnchors(this.stripQuotes(query)));
+ break;
+ }
+
+ return str + query + ')';
+ }
+}
+
+
+@Injectable()
+export class EgCatalogService {
+
+ ccvmMap: {[ccvm:string] : EgIdlObject[]} = {};
+ cmfMap: {[cmf:string] : EgIdlObject[]} = {};
+
+ constructor(
+ private net: EgNetService,
+ private org: EgOrgService,
+ private unapi: EgUnapiService,
+ private pcrud: EgPcrudService
+ ) {}
+
+
+ search(context: CatalogContext): Promise<void> {
+
+ var fullQuery = context.compileSearch();
+
+ console.debug(`search query: ${fullQuery}`);
+
+ return new Promise((resolve, reject) => {
+
+ this.net.request(
+ 'open-ils.search',
+ 'open-ils.search.biblio.multiclass.query.staff', {
+ limit : context.paginator.limit,
+ offset : context.paginator.offset
+ }, fullQuery, true
+ ).subscribe(result => {
+ context.result = result;
+ context.result.records = [];
+ context.paginator.resultCount = result.count;
+
+ let promises = [];
+ result.ids.forEach(blob => {
+ promises.push(
+ this.getBibSummary(blob[0],
+ context.searchOrg.id(),
+ context.global ?
+ context.org.root().ou_typ().depth() :
+ context.searchOrg.ou_type().depth()
+ ).then(
+ summary => context.result.records.push(summary)
+ )
+ );
+ });
+
+ Promise.all(promises).then(ok => resolve());
+ });
+ })
+ }
+
+ fetchCcvms(): Promise<void> {
+
+ if (Object.keys(this.ccvmMap).length)
+ return Promise.resolve();
+
+ return new Promise((resolve, reject) => {
+ this.pcrud.search('ccvm',
+ {ctype : CCVM_FILTER_TYPES}, {}, {atomic: true}
+ ).subscribe(list => {
+ console.debug(list);
+ this.compileCcvms(list);
+ resolve();
+ })
+ });
+ }
+
+ compileCcvms(ccvms : EgIdlObject[]): void {
+ ccvms.forEach(ccvm => {
+ if (!this.ccvmMap[ccvm.ctype()])
+ this.ccvmMap[ccvm.ctype()] = [];
+ this.ccvmMap[ccvm.ctype()].push(ccvm);
+ });
+
+ Object.keys(this.ccvmMap).forEach(cType => {
+ this.ccvmMap[cType] =
+ this.ccvmMap[cType].sort((a, b) => {
+ return a.value() < b.value() ? -1 : 1;
+ });
+ });
+ }
+
+
+ fetchCmfs(): Promise<void> {
+ // At the moment, we only need facet CMFs.
+ if (Object.keys(this.cmfMap).length)
+ return Promise.resolve();
+
+ return new Promise((resolve, reject) => {
+ this.pcrud.search('cmf',
+ {facet_field : 't'}, {}, {atomic : true}
+ ).subscribe(
+ cmfs => {
+ cmfs.forEach(c => this.cmfMap[c.id()] = c);
+ resolve();
+ }
+ )
+ });
+ }
+
+ /**
+ * Probably don't want to require navigating the bare UNAPI
+ * blob in the template, plus that's quite a lot of stuff
+ * to sit in the scope / watch for changes. Translate the
+ * UNAPI content into a more digestable form.
+ * TODO: Add display field support
+ */
+ translateBibSummary(summary: any): any { // TODO: bib summary type
+ const UNAPI_PATHS = {
+ title : 'titleInfo[0].title[0]',
+ author: 'name[0].namePart[0]',
+ genre: 'genre[0]._'
+ }
+
+ let response = {};
+ for (let key in UNAPI_PATHS) {
+ try {
+ response[key] = eval(`summary.mods.${UNAPI_PATHS[key]}`);
+ } catch(E) {
+ response[key] ='';
+ }
+ }
+
+ return response;
+ }
+
+ getBibSummary(bibId: number, orgId: number, depth: number): Promise<any> {
+ return new Promise((resolve, reject) => {
+ this.unapi.getAsObject({
+ target: 'bre',
+ id: bibId,
+ extras: '{bre.extern,holdings_xml,mra}',
+ format: 'mods32',
+ orgId: orgId,
+ depth: depth
+ }).then(summary => {
+ summary = this.translateBibSummary(summary);
+ summary.id = bibId;
+ resolve(summary);
+ });
+ });
+ }
+
+}
--- /dev/null
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {EgCatalogComponent} from './catalog.component';
+import {EgCatalogSearchComponent} from './search.component';
+
+const routes: Routes = [{
+ path: '',
+ component: EgCatalogComponent,
+ children : [{
+ path: 'search',
+ component: EgCatalogSearchComponent,
+ }]
+}];
+
+@NgModule({
+ imports: [ RouterModule.forChild(routes) ],
+ exports: [ RouterModule ]
+})
+
+export class EgCatalogRoutingModule {}
--- /dev/null
+
+<h2>Search Sample</h2>
+
+<div *ngIf="context.result">
+ <div class="row" *ngFor="let record of context.result.records; let idx = index">
+ <div class="col-1">{{idx + 1}}</div>
+ <div class="col-3">{{record.title}}</div>
+ <div class="col-3">{{record.author}}</div>
+ <div class="col-3">{{record.genre}}</div>
+ </div>
+</div>
+
--- /dev/null
+import {Component, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {EgAuthService} from '@eg/core/auth';
+import {EgOrgService} from '@eg/core/org';
+import {EgCatalogService, CatalogContext} from './catalog.service';
+
+@Component({
+ templateUrl: 'search.component.html'
+})
+export class EgCatalogSearchComponent implements OnInit {
+
+ context: CatalogContext;
+
+ constructor(
+ private auth: EgAuthService,
+ private org: EgOrgService,
+ private cat: EgCatalogService
+ ) {}
+
+ ngOnInit() {
+ this.context = new CatalogContext();
+
+ this.cat.fetchCcvms().then(ok => { // TODO: catalog resolver
+ console.log(this.cat.ccvmMap);
+
+ this.context.searchClass = ['keyword'];
+ this.context.query = ['piano'];
+ this.context.joiner = ['&&'];
+ this.context.match = ['contains'];
+ this.context.format = null;
+ this.context.org = this.org; // hmm, refactor maybe
+ this.context.searchOrg = this.org.get(4); // BR1
+
+ this.cat.search(this.context).then(ok => {
+ console.log('ALL DONE SEARCH');
+ });
+ });
+ }
+}
+
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
-import { EgNetService } from '@eg/core/net.service';
+import { EgNetService } from '@eg/core/net';
import { EgAuthService } from '@eg/core/auth';
@Component({
import { Router, Resolve, RouterStateSnapshot,
ActivatedRouteSnapshot } from '@angular/router';
import { EgStoreService } from '@eg/core/store';
-import { EgNetService } from '@eg/core/net.service';
+import { EgNetService } from '@eg/core/net';
import { EgAuthService } from '@eg/core/auth';
/**
path: 'circ',
loadChildren : '@eg/staff/circ/circ.module#EgCircModule'
}, {
+ path: 'catalog',
+ loadChildren : '@eg/staff/catalog/catalog.module#EgCatalogModule'
+ }, {
path: 'admin',
loadChildren : '@eg/staff/admin/admin.module#EgAdminModule'
}]
Some links to test...
<a routerLink="/staff/admin/workstation/workstations">Workstation Admin</a>
+<br/>
+<br/>
+<a routerLink="/staff/catalog/search">Catalog Test</a>
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
import { EgAuthService, EgAuthWsState } from '@eg/core/auth';
-import { EgNetService } from '@eg/core/net.service';
+import { EgNetService } from '@eg/core/net';
@Component({
templateUrl: 'staff.component.html'