mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-18 07:23:03 +00:00
71894: MD field suggestions on edit item page
This commit is contained in:
@@ -1,9 +1,13 @@
|
|||||||
import { Component, Input, OnChanges, OnInit } from '@angular/core';
|
import { Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||||
|
import { MetadataSchemaDataService } from '../../../../core/data/metadata-schema-data.service';
|
||||||
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
|
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
|
||||||
|
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||||
import { hasValue, isNotEmpty } from '../../../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../../../shared/empty.util';
|
||||||
import { RegistryService } from '../../../../core/registry/registry.service';
|
import { RegistryService } from '../../../../core/registry/registry.service';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
||||||
import { map, take } from 'rxjs/operators';
|
import { map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
||||||
import { FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer';
|
import { FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer';
|
||||||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
||||||
@@ -11,6 +15,7 @@ import { NgModel } from '@angular/forms';
|
|||||||
import { MetadatumViewModel } from '../../../../core/shared/metadata.models';
|
import { MetadatumViewModel } from '../../../../core/shared/metadata.models';
|
||||||
import { MetadataField } from '../../../../core/metadata/metadata-field.model';
|
import { MetadataField } from '../../../../core/metadata/metadata-field.model';
|
||||||
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
|
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
|
||||||
|
import { followLink } from '../../../../shared/utils/follow-link-config.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// tslint:disable-next-line:component-selector
|
// tslint:disable-next-line:component-selector
|
||||||
@@ -60,6 +65,7 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
|
|||||||
constructor(
|
constructor(
|
||||||
private registryService: RegistryService,
|
private registryService: RegistryService,
|
||||||
private objectUpdatesService: ObjectUpdatesService,
|
private objectUpdatesService: ObjectUpdatesService,
|
||||||
|
private metadataSchemaService: MetadataSchemaDataService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,25 +134,53 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
|
|||||||
*/
|
*/
|
||||||
findMetadataFieldSuggestions(query: string): void {
|
findMetadataFieldSuggestions(query: string): void {
|
||||||
if (isNotEmpty(query)) {
|
if (isNotEmpty(query)) {
|
||||||
this.registryService.queryMetadataFields(query).pipe(
|
this.registryService.queryMetadataFields(query, null, followLink('schema')).pipe(
|
||||||
// getSucceededRemoteData(),
|
getSucceededRemoteData(),
|
||||||
take(1),
|
take(1),
|
||||||
map((data) => data.payload.page)
|
map((data) => data.payload.page),
|
||||||
).subscribe(
|
switchMap((fields: MetadataField[]) => {
|
||||||
(fields: MetadataField[]) => this.metadataFieldSuggestions.next(
|
return fields.map((field: MetadataField, index: number) => {
|
||||||
fields.map((field: MetadataField) => {
|
// Resolve the metadata field's schema if not already the case, to be able to form complete MD field name
|
||||||
return {
|
if (!hasValue(field.schemaResolved)) {
|
||||||
displayValue: field.toString().split('.').join('.​'),
|
field.schema.pipe(
|
||||||
value: field.toString()
|
getSucceededRemoteData(),
|
||||||
};
|
getRemoteDataPayload(),
|
||||||
})
|
map((schema: MetadataSchema) => {
|
||||||
)
|
field.schemaResolved = schema;
|
||||||
);
|
if (index == fields.length - 1) {
|
||||||
|
this.setInputSuggestions(fields);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
take(1)
|
||||||
|
).subscribe()
|
||||||
|
} else {
|
||||||
|
this.setInputSuggestions(fields);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
),
|
||||||
|
take(1)).subscribe();
|
||||||
} else {
|
} else {
|
||||||
this.metadataFieldSuggestions.next([]);
|
this.metadataFieldSuggestions.next([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the list of input suggestion with the given Metadata fields, which all require a resolved MetadataSchema
|
||||||
|
* @param fields list of Metadata fields, which all require a resolved MetadataSchema
|
||||||
|
*/
|
||||||
|
setInputSuggestions(fields: MetadataField[]) {
|
||||||
|
this.metadataFieldSuggestions.next(
|
||||||
|
fields.map((field: MetadataField) => {
|
||||||
|
const fieldNameWhole = field.schemaResolved.prefix + '.' + field.toString();
|
||||||
|
return {
|
||||||
|
displayValue: fieldNameWhole.split('.').join('.​'),
|
||||||
|
value: fieldNameWhole
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a user should be allowed to edit this field
|
* 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
|
* @return an observable that emits true when the user should be able to edit this field and false when they should not
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { dataService } from '../cache/builders/build-decorators';
|
import { dataService } from '../cache/builders/build-decorators';
|
||||||
import { DataService } from './data.service';
|
import { DataService } from './data.service';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
@@ -27,6 +28,7 @@ import { RequestParam } from '../cache/models/request-param.model';
|
|||||||
export class MetadataFieldDataService extends DataService<MetadataField> {
|
export class MetadataFieldDataService extends DataService<MetadataField> {
|
||||||
protected linkPath = 'metadatafields';
|
protected linkPath = 'metadatafields';
|
||||||
protected searchBySchemaLinkPath = 'bySchema';
|
protected searchBySchemaLinkPath = 'bySchema';
|
||||||
|
protected searchByFieldNameLinkPath = 'byFieldName';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
@@ -53,6 +55,29 @@ export class MetadataFieldDataService extends DataService<MetadataField> {
|
|||||||
return this.searchBy(this.searchBySchemaLinkPath, optionsWithSchema, ...linksToFollow);
|
return this.searchBy(this.searchBySchemaLinkPath, optionsWithSchema, ...linksToFollow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find metadata fields with either the partial metadata field name (e.g. "dc.ti") as query or an exact match to
|
||||||
|
* at least the schema, element or qualifier
|
||||||
|
* @param schema optional; an exact match of the prefix of the metadata schema (e.g. "dc", "dcterms", "eperson")
|
||||||
|
* @param element optional; an exact match of the field's element (e.g. "contributor", "title")
|
||||||
|
* @param qualifier optional; an exact match of the field's qualifier (e.g. "author", "alternative")
|
||||||
|
* @param query optional (if any of schema, element or qualifier used) - part of the fully qualified field,
|
||||||
|
* should start with the start of the schema (e.g. "dc.ti")
|
||||||
|
* @param options The options info used to retrieve the fields
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||||
|
*/
|
||||||
|
findByFieldName(schema: string, element: string, qualifier: string, query: string, options: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<MetadataField>>) {
|
||||||
|
const optionParams = Object.assign(new FindListOptions(), options, {
|
||||||
|
searchParams: [
|
||||||
|
new RequestParam('schema', hasValue(schema) ? schema : ''),
|
||||||
|
new RequestParam('element', hasValue(element) ? element : ''),
|
||||||
|
new RequestParam('qualifier', hasValue(qualifier) ? qualifier : ''),
|
||||||
|
new RequestParam('query', hasValue(query) ? query : '')
|
||||||
|
]
|
||||||
|
});
|
||||||
|
return this.searchBy(this.searchByFieldNameLinkPath, optionParams, ...linksToFollow);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all metadata field requests
|
* Clear all metadata field requests
|
||||||
* Used for refreshing lists after adding/updating/removing a metadata field from a metadata schema
|
* Used for refreshing lists after adding/updating/removing a metadata field from a metadata schema
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
import { autoserialize, deserialize } from 'cerialize';
|
import { autoserialize, deserialize } from 'cerialize';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { map } from 'rxjs/operators';
|
||||||
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||||
import { link, typedObject } from '../cache/builders/build-decorators';
|
import { link, typedObject } from '../cache/builders/build-decorators';
|
||||||
|
import { MetadataSchemaDataService } from '../data/metadata-schema-data.service';
|
||||||
import { GenericConstructor } from '../shared/generic-constructor';
|
import { GenericConstructor } from '../shared/generic-constructor';
|
||||||
import { HALLink } from '../shared/hal-link.model';
|
import { HALLink } from '../shared/hal-link.model';
|
||||||
import { HALResource } from '../shared/hal-resource.model';
|
import { HALResource } from '../shared/hal-resource.model';
|
||||||
|
import { getRemoteDataPayload, getSucceededRemoteData } from '../shared/operators';
|
||||||
import { ResourceType } from '../shared/resource-type';
|
import { ResourceType } from '../shared/resource-type';
|
||||||
import { excludeFromEquals } from '../utilities/equals.decorators';
|
import { excludeFromEquals } from '../utilities/equals.decorators';
|
||||||
import { METADATA_FIELD } from './metadata-field.resource-type';
|
import { METADATA_FIELD } from './metadata-field.resource-type';
|
||||||
@@ -67,9 +70,11 @@ export class MetadataField extends ListableObject implements HALResource {
|
|||||||
@link(METADATA_SCHEMA)
|
@link(METADATA_SCHEMA)
|
||||||
schema?: Observable<RemoteData<MetadataSchema>>;
|
schema?: Observable<RemoteData<MetadataSchema>>;
|
||||||
|
|
||||||
|
schemaResolved?: MetadataSchema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to print this metadata field as a string
|
* Method to print this metadata field as a string without the schema
|
||||||
* @param separator The separator between the schema, element and qualifier in the string
|
* @param separator The separator between element and qualifier in the string
|
||||||
*/
|
*/
|
||||||
toString(separator: string = '.'): string {
|
toString(separator: string = '.'): string {
|
||||||
let key = this.element;
|
let key = this.element;
|
||||||
@@ -79,6 +84,28 @@ export class MetadataField extends ListableObject implements HALResource {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to print this metadata field as a string
|
||||||
|
* @param separator The separator between the schema, element and qualifier in the string
|
||||||
|
*/
|
||||||
|
toStringWithSchema(separator: string = '.', schemaService: MetadataSchemaDataService): Observable<string> {
|
||||||
|
let schemaObject: Observable<RemoteData<MetadataSchema>> = this.schema;
|
||||||
|
if (!hasValue(this.schema)) {
|
||||||
|
schemaObject = schemaService.findByHref(this._links.schema.href);
|
||||||
|
}
|
||||||
|
return schemaObject.pipe(
|
||||||
|
getSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
map((schemaPayload: MetadataSchema) => {
|
||||||
|
let key = this.element;
|
||||||
|
if (isNotEmpty(this.qualifier)) {
|
||||||
|
key += separator + this.qualifier;
|
||||||
|
}
|
||||||
|
return schemaPayload.namespace + separator + key;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that returns as which type of object this object should be rendered
|
* Method that returns as which type of object this object should be rendered
|
||||||
*/
|
*/
|
||||||
|
@@ -297,13 +297,10 @@ export class RegistryService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a filtered paginated list of metadata fields
|
* Retrieve a filtered paginated list of metadata fields
|
||||||
* @param query {string} The query to filter the field names by
|
* @param query {string} The query to use for the metadata field name, can be partial (e.g. "dc.ti")
|
||||||
* @returns an observable that emits a remote data object with a page of metadata fields that match the query
|
* @returns an observable that emits a remote data object with a page of metadata fields that match the query
|
||||||
*/
|
*/
|
||||||
// TODO this is temporarily disabled. The performance is too bad.
|
queryMetadataFields(query: string, options: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<MetadataField>>): Observable<RemoteData<PaginatedList<MetadataField>>> {
|
||||||
// Querying metadatafields will need to be implemented as a search endpoint on the rest api,
|
return this.metadataFieldService.findByFieldName(null, null, null, query, options, ...linksToFollow);
|
||||||
// not by downloading everything and preforming the query client side.
|
|
||||||
queryMetadataFields(query: string): Observable<RemoteData<PaginatedList<MetadataField>>> {
|
|
||||||
return createSuccessfulRemoteDataObject$(new PaginatedList<MetadataField>(null, []));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user