<class id="cza" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::z3950_attr" oils_persist:tablename="config.z3950_attr" reporter:label="Z39.50 Attribute">
<fields oils_persist:primary="id" oils_persist:sequence="config.z3950_attr_id_seq">
<field reporter:label="Z39.50 Attribute ID" name="id" reporter:datatype="id" reporter:selector="label"/>
- <field reporter:label="Z39.50 Source" name="source" reporter:datatype="link"/>
+ <field reporter:label="Z39.50 Source" name="source" reporter:datatype="link" config_field="true"/>
<field reporter:label="Name" name="name" reporter:datatype="text"/>
<field reporter:label="Label" name="label" reporter:datatype="text" oils_persist:i18n="true"/>
<field reporter:label="Code" name="code" reporter:datatype="int"/>
<field reporter:label="Always Use?" name="forceto" reporter:datatype="bool"/>
<field reporter:label="Current Ceiling Date" name="ceiling_date" reporter:datatype="timestamp"/>
<field reporter:label="Owner" name="owner" reporter:datatype="org_unit"/>
+ <field reporter:label="Values" name="values" oils_persist:virtual="true"
+ reporter:datatype="link" config_field="true"/>
</fields>
<links>
<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="values" reltype="has_many" key="hard_due_date" map="" class="chddv"/>
</links>
<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
<actions>
<class id="chddv" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::hard_due_date_values" oils_persist:tablename="config.hard_due_date_values" reporter:label="Hard Due Date Values">
<fields oils_persist:primary="id" oils_persist:sequence="config.hard_due_date_values_id_seq">
<field reporter:label="ID" name="id" reporter:datatype="id"/>
- <field reporter:label="Hard Due Date" name="hard_due_date" reporter:datatype="link"/>
+ <field reporter:label="Hard Due Date" name="hard_due_date" reporter:datatype="link" config_field="true"/>
<field reporter:label="Ceiling Date" name="ceiling_date" reporter:datatype="timestamp"/>
<field reporter:label="Active Date" name="active_date" reporter:datatype="timestamp"/>
</fields>
import {Component, Input, OnInit, TemplateRef, ViewChild} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
+import {Location} from '@angular/common';
import {IdlService, IdlObject} from '@eg/core/idl.service';
-import {GridDataSource} from '@eg/share/grid/grid';
+import {FormatService} from '@eg/core/format.service';
+import {GridDataSource, GridColumn} from '@eg/share/grid/grid';
import {GridComponent} from '@eg/share/grid/grid.component';
import {TranslateComponent} from '@eg/share/translate/translate.component';
import {ToastService} from '@eg/share/toast/toast.service';
// Override default values for fm-editor
@Input() defaultNewRecord: IdlObject;
+ // Used as the first part of the routerLink path when creating
+ // links to related tables via configField's.
+ @Input() configLinkBasePath: string;
@ViewChild('grid', { static: true }) grid: GridComponent;
@ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
idlClassDef: any;
pkeyField: string;
- configFields: string[];
+ configFields: any[]; // IDL field definitions
// True if any columns on the object support translations
translateRowIdx: number;
constructor(
private route: ActivatedRoute,
+ private ngLocation: Location,
+ private format: FormatService,
public idl: IdlService,
private org: OrgService,
public auth: AuthService,
}
ngOnInit() {
+
this.idlClassDef = this.idl.classes[this.idlClass];
this.pkeyField = this.idlClassDef.pkey || 'id';
this.idlClassDef.table;
}
+
+ // Note the field filter could be based purely on fields
+ // which are links, but that leads to cases where links
+ // are created to tables which are too big and/or admin
+ // interfaces which are not otherwise used because they
+ // have custom UI's instead.
+ // this.idlClassDef.fields.filter(f => f.datatype === 'link');
this.configFields =
this.idlClassDef.fields.filter(f => f.config_field);
const search: any = {};
- search[this.orgField] = this.searchOrgs.orgIds || [this.contextOrg.id()];
+ if (this.orgField) {
+ search[this.orgField] =
+ this.searchOrgs.orgIds || [this.contextOrg.id()];
+ }
if (this.gridFilters) {
// Lay the URL grid filters over our search object.
this.translator.open({size: 'lg'});
}
+
+ // Construct a routerLink path for a configField.
+ configFieldRouteLink(row: any, col: GridColumn): string {
+ const cf = this.configFields.filter(field => field.name === col.name)[0];
+ const linkClass = this.idl.classes[cf['class']];
+ const pathParts = linkClass.table.split(/\./); // schema.tablename
+ return `${this.configLinkBasePath}/${pathParts[0]}/${pathParts[1]}`;
+ }
+
+ // Compiles a gridFilter value used when navigating to a linked
+ // class via configField. The filter ensures the linked page
+ // only shows rows which refer back to the object from which the
+ // link was clicked.
+ configFieldRouteParams(row: any, col: GridColumn): any {
+ const cf = this.configFields.filter(field => field.name === col.name)[0];
+ let value = this.configFieldLinkedValue(row, col);
+
+ // For certain has-a relationships, the linked object will be
+ // fleshed so its display (selector) value can be used.
+ // Extract the scalar value found at the remote target field.
+ if (value && typeof value === 'object') { value = value[cf.key](); }
+
+ const filter: any = {};
+ filter[cf.key] = value;
+
+ return {gridFilters : JSON.stringify(filter)};
+ }
+
+ // Returns the value on the local object for the field which
+ // refers to the remote object. This may be a scalar or a
+ // fleshed IDL object.
+ configFieldLinkedValue(row: any, col: GridColumn): any {
+ const cf = this.configFields.filter(field => field.name === col.name)[0];
+ const linkClass = this.idl.classes[cf['class']];
+
+ // cf.key is the name of the field on the linked object that matches
+ // the value on our local object.
+ // In as has_many relationship, the remote field has its own
+ // 'key' value which determines which field on the local object
+ // represents the other end of the relationship. This is
+ // typically, but not always the local pkey field.
+
+ const localField =
+ cf.reltype === 'has_many' ?
+ (linkClass.field_map[cf.key].key || this.pkeyField) : cf.name;
+
+ return row[localField]();
+ }
+
+ // Returns a URL suitable for using as an href.
+ // We use an href to jump to the secondary admin page because
+ // routerLink within the same base component results in component
+ // reuse of a series of components which were not designed with
+ // reuse in mind.
+ configFieldLinkUrl(row: any, col: GridColumn): string {
+ const path = this.configFieldRouteLink(row, col);
+ const filters = this.configFieldRouteParams(row, col);
+ const url = path + '?gridFilters=' +
+ encodeURIComponent(filters.gridFilters);
+
+ return this.ngLocation.prepareExternalUrl(url);
+ }
+
+ configLinkLabel(row: any, col: GridColumn): string {
+ const cf = this.configFields.filter(field => field.name === col.name)[0];
+
+ // Has-many links have no specific value to use for display
+ // so just use the column label.
+ if (cf.reltype === 'has_many') { return col.label; }
+
+ return this.format.transform({
+ value: row[col.name](),
+ idlClass: this.idlClass,
+ idlField: col.name
+ });
+ }
}