import { Component, Input, OnChanges, OnInit } from '@angular/core'; import { hasValue, isNotEmpty } from '../../../../shared/empty.util'; import { RegistryService } from '../../../../core/registry/registry.service'; import { cloneDeep } from 'lodash'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; import { map, take } from 'rxjs/operators'; import { MetadataField } from '../../../../core/metadata/metadatafield.model'; import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model'; import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; import { NgModel } from '@angular/forms'; import { MetadatumViewModel } from '../../../../core/shared/metadata.models'; @Component({ // tslint:disable-next-line:component-selector selector: '[ds-edit-in-place-field]', styleUrls: ['./edit-in-place-field.component.scss'], templateUrl: './edit-in-place-field.component.html', }) /** * Component that displays a single metadatum of an item on the edit page */ export class EditInPlaceFieldComponent implements OnInit, OnChanges { /** * The current field, value and state of the metadatum */ @Input() fieldUpdate: FieldUpdate; /** * The current url of this page */ @Input() url: string; /** * List of strings with all metadata field keys available */ @Input() metadataFields: string[]; /** * The metadatum of this field */ metadata: MetadatumViewModel; /** * Emits whether or not this field is currently editable */ editable: Observable; /** * Emits whether or not this field is currently valid */ valid: Observable; /** * The current suggestions for the metadatafield when editing */ metadataFieldSuggestions: BehaviorSubject = new BehaviorSubject([]); constructor( private metadataFieldService: RegistryService, private objectUpdatesService: ObjectUpdatesService, ) { } /** * Sets up an observable that keeps track of the current editable and valid state of this field */ ngOnInit(): void { this.editable = this.objectUpdatesService.isEditable(this.url, this.metadata.uuid); this.valid = this.objectUpdatesService.isValid(this.url, this.metadata.uuid); } /** * Sends a new change update for this field to the object updates service */ update(ngModel?: NgModel) { this.objectUpdatesService.saveChangeFieldUpdate(this.url, this.metadata); if (hasValue(ngModel)) { this.checkValidity(ngModel); } } /** * Method to check the validity of a form control * @param ngModel */ private checkValidity(ngModel: NgModel) { ngModel.control.setValue(ngModel.viewModel); ngModel.control.updateValueAndValidity(); this.objectUpdatesService.setValidFieldUpdate(this.url, this.metadata.uuid, ngModel.control.valid); } /** * Sends a new editable state for this field to the service to change it * @param editable The new editable state for this field */ setEditable(editable: boolean) { this.objectUpdatesService.setEditableFieldUpdate(this.url, this.metadata.uuid, editable); } /** * Sends a new remove update for this field to the object updates service */ remove() { this.objectUpdatesService.saveRemoveFieldUpdate(this.url, this.metadata); } /** * Notifies the object updates service that the updates for the current field can be removed */ removeChangesFromField() { this.objectUpdatesService.removeSingleFieldUpdate(this.url, this.metadata.uuid); } /** * Sets the current metadatafield based on the fieldUpdate input field */ ngOnChanges(): void { this.metadata = cloneDeep(this.fieldUpdate.field) as MetadatumViewModel; } /** * Requests all metadata fields that contain the query string in their key * Then sets all found metadata fields as metadataFieldSuggestions * @param query The query to look for */ findMetadataFieldSuggestions(query: string): void { if (isNotEmpty(query)) { this.metadataFieldService.queryMetadataFields(query).pipe( // getSucceededRemoteData(), take(1), map((data) => data.payload.page) ).subscribe( (fields: MetadataField[]) => this.metadataFieldSuggestions.next( fields.map((field: MetadataField) => { return { displayValue: field.toString().split('.').join('.​'), value: field.toString() }; }) ) ); } else { this.metadataFieldSuggestions.next([]); } } /** * Check if a user should be allowed to edit this field * @return an observable that emits true when the user should be able to edit this field and false when they should not */ canSetEditable(): Observable { return this.editable.pipe( map((editable: boolean) => { if (editable) { return false; } else { return this.fieldUpdate.changeType !== FieldChangeType.REMOVE; } }) ); } /** * Check if a user should be allowed to disabled editing this field * @return an observable that emits true when the user should be able to disable editing this field and false when they should not */ canSetUneditable(): Observable { return this.editable; } /** * Check if a user should be allowed to remove this field * @return an observable that emits true when the user should be able to remove this field and false when they should not */ canRemove(): Observable { return observableOf(this.fieldUpdate.changeType !== FieldChangeType.REMOVE && this.fieldUpdate.changeType !== FieldChangeType.ADD); } /** * Check if a user should be allowed to undo changes to this field * @return an observable that emits true when the user should be able to undo changes to this field and false when they should not */ canUndo(): Observable { return this.editable.pipe( map((editable: boolean) => this.fieldUpdate.changeType >= 0 || editable) ); } protected isNotEmpty(value): boolean { return isNotEmpty(value); } }