--- /dev/null
+import {Injectable} 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';
+import {CatalogSearchContext} from './search-context';
+
+const CCVM_FILTER_TYPES = [
+ 'item_type',
+ 'item_form',
+ 'item_lang',
+ 'audience',
+ 'audience_group',
+ 'vr_format',
+ 'bib_level',
+ 'lit_form',
+ 'search_format'
+];
+
+@Injectable()
+export class EgCatalogService {
+
+ searchContext: CatalogSearchContext;
+ ccvmMap: {[ccvm:string] : EgIdlObject[]} = {};
+ cmfMap: {[cmf:string] : EgIdlObject[]} = {};
+
+ constructor(
+ private net: EgNetService,
+ private org: EgOrgService,
+ private unapi: EgUnapiService,
+ private pcrud: EgPcrudService
+ ) {
+ this.searchContext = new CatalogSearchContext();
+ this.searchContext.org = this.org;
+ }
+
+ search(context: CatalogSearchContext): 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.pager.limit,
+ offset : context.pager.offset
+ }, fullQuery, true
+ ).subscribe(result => {
+ context.result = result;
+ context.result.records = [];
+ context.pager.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 => {
+ 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();
+ }
+ )
+ });
+ }
+
+
+ /**
+ * Bib record via UNAPI as mods32 (for now) with holdings summary
+ * and record attributes.
+ */
+ 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);
+ });
+ });
+ }
+
+ /**
+ * 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 interface
+ 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;
+ }
+}
--- /dev/null
+import {EgOrgService} from '@eg/core/org';
+import {EgIdlObject} from '@eg/core/idl';
+import {Pager} from '@eg/share/util/pager';
+import {Params} from '@angular/router';
+
+
+// Document and enforce facet filter entries.
+interface FacetFilter {
+ facetClass: string;
+ facetName: string;
+ facetValue: string;
+}
+
+// Not an angular service.
+// It's conceviable there could be multiple contexts.
+export class CatalogSearchContext {
+
+ // Search options and filters
+ available: boolean;
+ global: boolean;
+ sort: string;
+ searchClass: string[];
+ query: string[];
+ joiner: string[];
+ match: string[];
+ format: string;
+ searchOrg: EgIdlObject;
+ ccvmFilters: {[ccvmCode:string] : string};
+ facetFilters: FacetFilter[];
+
+ // Result from most recent search.
+ result: any;
+
+ // Utility stuff
+ pager: Pager;
+ org: EgOrgService;
+
+ constructor() {
+ this.pager = new Pager();
+ this.reset();
+ }
+
+ reset(): void {
+ this.pager.offset = 0,
+ this.format = '',
+ this.sort = '',
+ this.query = [''];
+ this.searchClass = ['keyword'];
+ this.match = ['contains'];
+ this.joiner = [''];
+ this.available = false;
+ this.global = false;
+ this.ccvmFilters = {};
+ this.facetFilters = [];
+ }
+
+ 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 + ')';
+ }
+
+ /**
+ * Returns a URL query structure suitable for using with
+ * router.navigate(..., {queryParams:...}).
+ * No navigation is performed within.
+ */
+ toUrl(): any {
+ let query = {};
+
+ return query;
+ }
+
+ /**
+ * Absorbs URL values from the active route and applies them to
+ * this search context instance.
+ */
+ fromUrl(params: Params): void {
+ //queryParamMap
+ }
+}
+
+
--- /dev/null
+
+/**
+ * Utility class for manage paged information.
+ */
+export class Pager {
+ 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;
+ }
+}
+<!-- search form sits atop every catalog page -->
<eg-catalog-search-form></eg-catalog-search-form>
<router-outlet></router-outlet>
templateUrl: 'catalog.component.html'
})
export class EgCatalogComponent implements OnInit {
- constructor() {}
- ngOnInit() { }
+ constructor(private route: ActivatedRoute) {}
+
+ ngOnInit() {
+ }
}
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 {EgCatalogRoutingModule} from './routing.module';
+import {EgCatalogService} from '@eg/share/catalog/catalog.service';
import {EgCatalogComponent} from './catalog.component';
-import {EgCatalogSearchFormComponent} from './search-form.component';
-import {EgCatalogResultsComponent} from './results.component';
-import {EgCatalogService} from './catalog.service';
+import {SearchFormComponent} from './search-form.component';
+import {ResultsComponent} from './result/results.component';
+import {ResultPaginationComponent} from './result/pagination.component';
+import {ResultFacetsComponent} from './result/facets.component';
+import {ResultRecordComponent}
+ from './result/record.component';
@NgModule({
declarations: [
EgCatalogComponent,
- EgCatalogSearchFormComponent,
- EgCatalogResultsComponent
+ ResultsComponent,
+ SearchFormComponent,
+ ResultRecordComponent,
+ ResultFacetsComponent,
+ ResultPaginationComponent
],
imports: [
EgStaffModule,
+++ /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';
-import {SearchContext} from './search-context';
-
-const CCVM_FILTER_TYPES = [
- 'item_type',
- 'item_form',
- 'item_lang',
- 'audience',
- 'audience_group',
- 'vr_format',
- 'bib_level',
- 'lit_form',
- 'search_format'
-];
-
-@Injectable()
-export class EgCatalogService {
-
- searchContext: SearchContext;
- ccvmMap: {[ccvm:string] : EgIdlObject[]} = {};
- cmfMap: {[cmf:string] : EgIdlObject[]} = {};
-
- constructor(
- private net: EgNetService,
- private org: EgOrgService,
- private unapi: EgUnapiService,
- private pcrud: EgPcrudService
- ) {
- this.searchContext = new SearchContext();
- this.searchContext.org = this.org;
- }
-
- search(context: SearchContext): 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.pager.limit,
- offset : context.pager.offset
- }, fullQuery, true
- ).subscribe(result => {
- context.result = result;
- context.result.records = [];
- context.pager.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 => {
- 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);
- });
- });
- }
-
-}
import {EgNetService} from '@eg/core/net';
import {EgAuthService} from '@eg/core/auth';
import {EgPcrudService} from '@eg/core/pcrud';
-import {EgCatalogService} from './catalog.service'
+import {EgCatalogService} from '@eg/share/catalog/catalog.service';
@Injectable()
export class EgCatalogResolver implements Resolve<Promise<any[]>> {
--- /dev/null
+TEST FACETS
--- /dev/null
+import {Component, OnInit, Input} from '@angular/core';
+import {EgCatalogService} from '@eg/share/catalog/catalog.service';
+import {CatalogSearchContext} from '@eg/share/catalog/search-context';
+
+@Component({
+ selector: 'eg-catalog-result-facets',
+ styleUrls: ['facets.component.css'],
+ templateUrl: 'facets.component.html'
+})
+export class ResultFacetsComponent implements OnInit {
+
+ searchContext: CatalogSearchContext;
+
+ constructor(
+ private cat: EgCatalogService
+ ) {}
+
+ ngOnInit() {
+ this.searchContext = this.cat.searchContext;
+ }
+
+}
+
+
--- /dev/null
+TEST PAGINATION
--- /dev/null
+import {Component, OnInit, Input} from '@angular/core';
+import {EgCatalogService} from '@eg/share/catalog/catalog.service';
+import {CatalogSearchContext} from '@eg/share/catalog/search-context';
+
+@Component({
+ selector: 'eg-catalog-result-pagination',
+ styleUrls: ['pagination.component.css'],
+ templateUrl: 'pagination.component.html'
+})
+export class ResultPaginationComponent implements OnInit {
+
+ searchContext: CatalogSearchContext;
+
+ constructor(
+ private cat: EgCatalogService
+ ) {}
+
+ ngOnInit() {
+ this.searchContext = this.cat.searchContext;
+ }
+
+}
+
+
--- /dev/null
+
+<div class="row">
+ <div class="col-1">{{index + 1}}</div>
+ <div class="col-3">{{bibSummary.title}}</div>
+ <div class="col-3">{{bibSummary.author}}</div>
+ <div class="col-3">{{bibSummary.genre}}</div>
+</div>
+
--- /dev/null
+import {Component, OnInit, Input} from '@angular/core';
+import {EgCatalogService} from '@eg/share/catalog/catalog.service';
+import {CatalogSearchContext} from '@eg/share/catalog/search-context';
+
+@Component({
+ selector: 'eg-catalog-result-record',
+ styleUrls: ['record.component.css'],
+ templateUrl: 'record.component.html'
+})
+export class ResultRecordComponent implements OnInit {
+
+ @Input() index: number; // 0-index display row
+ @Input() bibSummary: any;
+ searchContext: CatalogSearchContext;
+
+ constructor(
+ private cat: EgCatalogService
+ ) {}
+
+ ngOnInit() {
+ this.searchContext = this.cat.searchContext;
+ }
+
+}
+
+
--- /dev/null
+
+<div class="row">
+ <div class="col-2">
+ <!-- facets -->
+ </div>
+ <div class="col-10">
+ <div *ngIf="searchContext.result">
+ <div *ngFor="let bibSummary of searchContext.result.records; let idx = index">
+ <eg-catalog-result-record [bibSummary]="bibSummary" [index]="idx">
+ </eg-catalog-result-record>
+ </div>
+ </div>
+ </div>
+</div>
+
--- /dev/null
+import {Component, OnInit, Input} from '@angular/core';
+import {EgCatalogService} from '@eg/share/catalog/catalog.service';
+import {CatalogSearchContext} from '@eg/share/catalog/search-context';
+
+@Component({
+ selector: 'eg-catalog-results',
+ styleUrls: ['results.component.css'],
+ templateUrl: 'results.component.html'
+})
+export class ResultsComponent implements OnInit {
+
+ searchContext: CatalogSearchContext;
+
+ constructor(
+ private cat: EgCatalogService
+ ) {}
+
+ ngOnInit() {
+ this.searchContext = this.cat.searchContext;
+ }
+
+}
+
+
+++ /dev/null
-
-<h2>Search Sample</h2>
-
-<div *ngIf="searchContext.result">
- <div class="row"
- *ngFor="let record of searchContext.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} from './catalog.service';
-import {SearchContext} from './search-context';
-
-@Component({
- templateUrl: 'results.component.html'
-})
-export class EgCatalogResultsComponent implements OnInit {
-
- searchContext: SearchContext;
-
- constructor(
- private auth: EgAuthService,
- private org: EgOrgService,
- private cat: EgCatalogService
- ) {
- this.searchContext = this.cat.searchContext;
- }
-
- ngOnInit() {
- }
-}
-
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {EgCatalogComponent} from './catalog.component';
-import {EgCatalogResultsComponent} from './results.component';
+import {ResultsComponent} from './result/results.component';
import {EgCatalogResolver} from './resolver.service';
const routes: Routes = [{
resolve: {catResolver : EgCatalogResolver},
children : [{
path: 'search',
- component: EgCatalogResultsComponent,
+ component: ResultsComponent,
}]
}];
+++ /dev/null
-import {EgOrgService} from '@eg/core/org';
-import {EgIdlObject} from '@eg/core/idl';
-
-class Pager {
- 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;
- }
-}
-
-interface FacetFilter {
- facetClass: string;
- facetName: string;
- facetValue: string;
-}
-
-
-export class SearchContext {
- available: boolean;
- global: boolean;
- sort: string;
- searchClass: string[];
- query: string[];
- joiner: string[];
- match: string[];
- format: string;
- searchOrg: EgIdlObject;
- ccvmFilters: {[ccvm:string] : string};
- facetFilters: FacetFilter[];
- pager: Pager;
- org: EgOrgService;
- result: any;
-
- constructor() {
- this.pager = new Pager();
- this.reset();
- }
-
- reset(): void {
- this.pager.offset = 0,
- this.format = '',
- this.sort = '',
- this.query = [''];
- this.searchClass = ['keyword'];
- this.match = ['contains'];
- this.joiner = [''];
- this.available = false;
- this.global = false;
- this.ccvmFilters = {};
- this.facetFilters = [];
- }
-
- 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 + ')';
- }
-}
-
-
--- /dev/null
+#staffcat-search-form .eg-org-selector,
+#staffcat-search-form .eg-org-selector button {
+ width: 100%;
+ text-align: left
+}
+.flex-row {
+ display: flex;
+}
+.flex-cell {
+ /* override style.css padding:4px */
+ flex: 1;
+ padding: 0px 0px 0px 8px;
+}
+.flex-2 {
+ flex: 2
+}
+.flex-row:first-child {
+ padding-left: 0px;
+}
+
-
-<style>
- /** TODO: clean up some inline styles */
-
- #staffcat-search-form .eg-org-selector,
- #staffcat-search-form .eg-org-selector button {
- width: 100%;
- text-align: left
- }
- .flex-row {
- display: flex;
- }
- .flex-cell {
- /* override style.css padding:4px */
- flex: 1;
- padding: 0px 0px 0px 8px;
- }
- .flex-2 {
- flex: 2
- }
- .flex-row:first-child {
- padding-left: 0px;
- }
-</style>
<div id='staffcat-search-form'>
<div class="row"
*ngFor="let q of searchContext.query; let idx = index; trackBy:trackByIdx">
</div>
<!--
- <div class="col-3">
- <div *ngIf="idx == 0" class="pull-right">
- <button class="btn btn-success" type="button"
- ng-click="searchContext.offset=0;searchContext.search_by_form()">
- [% l('Search') %]
- </button>
- <button class="btn btn-warning" type="button"
- ng-click="searchContext.reset_form()">
- [% l('Clear Form') %]
- </button>
- <button class="btn btn-default" type="button"
- *ngIf="!showAdvancedSearch"
- ng-click="showAdvancedSearch=true">
- [% l('More Filters...') %]
- </button>
- <button class="btn btn-default" type="button"
- *ngIf="showAdvancedSearch"
- ng-click="showAdvancedSearch=false">
- [% l('Hide Filters') %]
- </button>
- </div>
- </div>
- </div>
<div class="row">
<div class="col-9 flex-row">
<div class="flex-cell">
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} from './catalog.service';
-import {SearchContext} from './search-context';
import {EgIdlObject} from '@eg/core/idl';
+import {EgOrgService} from '@eg/core/org';
+import {EgCatalogService} from '@eg/share/catalog/catalog.service';
+import {CatalogSearchContext} from '@eg/share/catalog/search-context';
@Component({
selector: 'eg-catalog-search-form',
+ styleUrls: ['search-form.component.css'],
templateUrl: 'search-form.component.html'
})
-export class EgCatalogSearchFormComponent implements OnInit {
+export class SearchFormComponent implements OnInit {
- searchContext: SearchContext;
ccvmMap: {[ccvm:string] : EgIdlObject[]} = {};
cmfMap: {[cmf:string] : EgIdlObject[]} = {};
+ searchContext: CatalogSearchContext;
showAdvancedSearch: boolean = false;
constructor(
- private auth: EgAuthService,
+ private route: ActivatedRoute,
private org: EgOrgService,
private cat: EgCatalogService
) {}
ngOnInit() {
- this.searchContext = this.cat.searchContext;
this.ccvmMap = this.cat.ccvmMap;
this.cmfMap = this.cat.cmfMap;
+ this.searchContext = this.cat.searchContext;
+
+ // Sync the context with the load-time URL params and launch
+ // a new search if necessary.
+ this.searchContext.fromUrl(this.route.snapshot.queryParams);
+ this.searchByUrl();
}
addSearchRow(index: number): void {
}
checkEnter($event: any): void {
- console.log($event.keyCode);
if ($event.keyCode == 13) {
this.searchContext.pager.offset = 0;
this.searchByForm();
}
searchByForm(): void {
- console.log('searchByForm()');
- this.searchContext.searchOrg = this.org.get(4); // TEST BR1
+ this.searchContext.searchOrg = this.org.get(4); // TODO: TEST BR1
this.cat.search(this.searchContext).then(ok => {
console.debug('search complete');
});
+ }
+ searchByUrl(): void {
}
}
<html lang="en">
<head>
<meta charset="utf-8">
- <title i18n="Page Title">Eg</title>
+ <title i18n="Page Title">AngEG</title>
<base href="/webby">
<meta name="viewport" content="width=device-width, initial-scale=1">