</ng-template>
<!-- this one is also repeated a lot -->
-<ng-template #batchAttr let-field="field"
+<ng-template #batchAttr let-field="field" let-required="required"
let-label="label" let-template="template" let-displayAs="displayAs">
<eg-batch-item-attr
[name]="field"
[label]="label || copyFieldLabel(field)"
+ [valueRequired]="required"
[displayAs]="displayAs"
[editInputDomId]="field + '-input'"
- [emptyIsUnset]="true"
[editTemplate]="template"
[labelCounts]="itemAttrCounts(field)"
(valueCleared)="applyCopyValue(field, null)"
<div class="col-lg-7 d-flex">
<button class="btn btn-outline-dark mr-2" (click)="applyTemplate()" i18n>Apply</button>
<button class="btn btn-outline-dark mr-2" (click)="saveTemplate()" i18n>Save</button>
- <!--
- <button class="btn btn-outline-dark mr-2" (click)="importTemplate()" i18n>Import</button>
- -->
<!--
- The type typical approach of wrapping a file input in a <label>
- results in button-ish things that have slightly different dimensions
+ The typical approach of wrapping a file input in a <label> results
+ in button-ish things that have slightly different dimensions.
+ Instead have a button activate a hidden file input.
-->
<button class="btn btn-outline-dark mr-2" (click)="templateFile.click()">
<input type="file" class="d-none" #templateFile
<div class="mb-1" *ngIf="displayAttr('barcode')">
<eg-batch-item-attr label="Barcode" i18n-label
- [readOnly]="true" [emptyIsUnset]="true"
- [labelCounts]="itemAttrCounts('barcode')">
+ [readOnly]="true" [labelCounts]="itemAttrCounts('barcode')">
</eg-batch-item-attr>
</div>
</eg-item-location-select>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'location',template:locationTemplate}">
+ context:{field:'location',required:true,template:locationTemplate}">
</ng-container>
</div>
</eg-org-select>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'circ_lib',template:circLibTemplate}">
+ context:{field:'circ_lib',required:true,template:circLibTemplate}">
</ng-container>
</div>
</eg-org-select>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'owning_lib',template:owningLibTemplate,label:olLabel.text}">
+ context:{field:'owning_lib',required:true,template:owningLibTemplate,label:olLabel.text}">
</ng-container>
</div>
</ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'circulate',template:circulateTemplate,displayAs:'bool'}">
+ context:{field:'circulate',required:true,template:circulateTemplate,displayAs:'bool'}">
</ng-container>
</div>
</ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'holdable',template:holdableTemplate,displayAs:'bool'}">
+ context:{field:'holdable',required:true,template:holdableTemplate,displayAs:'bool'}">
</ng-container>
</div>
<ng-template #loanDurationTemplate>
<select class="form-control"
- id="loan-duration-input" [(ngModel)]="values['loan_duration']">
+ id="loan_duration-input" [(ngModel)]="values['loan_duration']">
<option value="1" i18n>{{loanDurationShort.text}}</option>
<option value="2" i18n>{{loanDurationNormal.text}}</option>
<option value="3" i18n>{{loanDurationLong.text}}</option>
</select>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'loan_duration',template:loanDurationTemplate}">
+ context:{field:'loan_duration',required:true,template:loanDurationTemplate}">
</ng-container>
</div>
<ng-template #fineLevelTemplate>
<select class="form-control"
- id="fine-level-input" [(ngModel)]="values['fine_level']">
+ id="fine_level-input" [(ngModel)]="values['fine_level']">
<option value="1" i18n>{{fineLevelLow.text}}</option>
<option value="2" i18n>{{fineLevelNormal.text}}</option>
<option value="3" i18n>{{fineLevelHigh.text}}</option>
</select>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'fine_level',template:fineLevelTemplate}">
+ context:{field:'fine_level',required:true,template:fineLevelTemplate}">
</ng-container>
</div>
<div *ngIf="displayAttr('circ_as_type')">
<ng-template #circAsTypeTemplate>
- <eg-combobox domId="circ-as-type-input"
+ <eg-combobox domId="circ_as_type-input"
(ngModelChange)="values['circ_as_type'] = $event ? $event.id : null"
[ngModel]="values['circ_as_type']">
<eg-combobox-entry *ngFor="let map of volcopy.commonData.acp_item_type_map"
<div *ngIf="displayAttr('circ_modifier')">
<ng-template #circModifierTemplate>
- <select class="form-control" [(ngModel)]="values['circ_modifier']">
+ <select class="form-control" id='circ_modifier-input'
+ [(ngModel)]="values['circ_modifier']">
<option [value]="null" i18n><Unset></option>
<option *ngFor="let mod of volcopy.commonData.acp_circ_modifier"
value="{{mod.code()}}">{{mod.name()}}</option>
</ng-template>
<eg-batch-item-attr label="Alert Message" i18n-label
editInputDomId="alert-message-input"
- [emptyIsUnset]="true"
[editTemplate]="alertMessageTemplate"
[labelCounts]="itemAttrCounts('alert_message')"
(changesSaved)="applyCopyValue('alert_message')">
</ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'deposit',template:depositTemplate,displayAs:'bool'}">
+ context:{field:'deposit',required:true,template:depositTemplate,displayAs:'bool'}">
</ng-container>
</div>
<div *ngIf="displayAttr('deposit_amount')">
<ng-template #depositAmountTemplate>
<input type="number" class="form-control"
- id="deposit-amount-input" [(ngModel)]="values['deposit_amount']"/>
+ id="deposit_amount-input" [(ngModel)]="values['deposit_amount']"/>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'deposit_amount',template:depositAmountTemplate,displayAs:'currency'}">
+ context:{field:'deposit_amount',required:true,template:depositAmountTemplate,displayAs:'currency'}">
</ng-container>
</div>
</ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'opac_visible',template:opacVisibleTemplate,displayAs:'bool'}">
+ context:{field:'opac_visible',required:true,template:opacVisibleTemplate,displayAs:'bool'}">
</ng-container>
</div>
</ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="batchAttr;
- context:{field:'ref',template:refTemplate,displayAs:'bool'}">
+ context:{field:'ref',required:true,template:refTemplate,displayAs:'bool'}">
</ng-container>
</div>
<ng-template #mintConditionTemplate>
<select class="form-control"
- id="mint-condition-input" [(ngModel)]="values['mint_condition']">
+ id="mint_condition-input" [(ngModel)]="values['mint_condition']">
<option value="t" i18n>{{mintConditionYes.text}}</option>
<option value="f" i18n>{{mintConditionNo.text}}</option>
</select>
</ng-template>
<eg-batch-item-attr label="{{cat.name()}} ({{orgSn(cat.owner())}})" i18n-label
name="stat_cat_{{cat.id()}}" editInputDomId="stat-cat-input-{{cat.id()}}"
- [emptyIsUnset]="true"
[editTemplate]="statCatTemplate"
[labelCounts]="statCatCounts(cat.id())"
(valueCleared)="statCatChanged(cat.id(), true)"
// Display only
@Input() readOnly = false;
- // If true, null/undefined/empty-string display as <Unset>
- @Input() emptyIsUnset: boolean;
+ // Warn the user when a required field has an empty value
+ @Input() valueRequired = false;
+
+ // If true, a value of '' is considered unset for display and
+ // valueRequired purposes.
+ @Input() emptyStringIsUnset = true;
// Lists larger than this will be partially hidden behind
// and expandy.
return Object.keys(this.labelCounts).length > 1;
}
- toggleEditMode() {
- if (this.readOnly) { return; }
-
- this.editing = !this.editing;
+ // True if a value is required and any value exists that's unset.
+ warnOnRequired(): boolean {
+ if (!this.valueRequired) { return false; }
- // Avoid using selectRootElement to focus.
- // https://stackoverflow.com/a/36059595
- if (this.editing) {
+ return Object.keys(this.labelCounts)
+ .filter(key => this.valueIsUnset(key)).length > 0;
+ }
- Object.keys(this.labelCounts).forEach(
- key => this.editValues[key] = true);
+ valueIsUnset(value: any): boolean {
+ return (
+ value === null ||
+ value === undefined ||
+ (this.emptyStringIsUnset && value === '')
+ );
+ }
- if (this.editInputDomId) {
- setTimeout(() => {
- const node = document.getElementById(this.editInputDomId);
- if (node) { node.focus(); }
- });
- }
+ enterEditMode() {
+ if (this.readOnly || this.editing) { return; }
+ this.editing = true;
+
+ // Assume all values should be edited by default
+ Object.keys(this.labelCounts).forEach(
+ key => this.editValues[key] = true);
+
+ if (this.editInputDomId) {
+ setTimeout(() => {
+ // Avoid using selectRootElement to focus.
+ // https://stackoverflow.com/a/36059595
+ const node = document.getElementById(this.editInputDomId);
+ if (node) { node.focus(); }
+ });
}
}
}