mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-12 04:23:04 +00:00
bug fixing
This commit is contained in:
@@ -175,7 +175,7 @@
|
||||
},
|
||||
"view": {
|
||||
"head": "View Item",
|
||||
"title": "Item Edit - Item"
|
||||
"title": "Item Edit - View"
|
||||
},
|
||||
"curate": {
|
||||
"head": "Curate",
|
||||
|
@@ -7,19 +7,20 @@
|
||||
<div *ngIf="(editable | async)" class="field-container">
|
||||
<ds-input-suggestions [suggestions]="(metadataFieldSuggestions | async)"
|
||||
[(ngModel)]="metadata.key"
|
||||
(submitSuggestion)="update(suggestionControl.control)"
|
||||
(clickSuggestion)="update(suggestionControl.control)"
|
||||
(typeSuggestion)="update(suggestionControl.control)"
|
||||
(blur)="checkValidity(suggestionControl.control)"
|
||||
(submitSuggestion)="update(suggestionControl)"
|
||||
(clickSuggestion)="update(suggestionControl)"
|
||||
(typeSuggestion)="update(suggestionControl)"
|
||||
(dsClickOutside)="checkValidity(suggestionControl)"
|
||||
(findSuggestions)="findMetadataFieldSuggestions($event)"
|
||||
#suggestionControl="ngModel"
|
||||
[dsInListValidator]="metadataFields | async"
|
||||
[valid]="(valid | async)"
|
||||
[dsInListValidator]="metadataFields"
|
||||
[valid]="(valid | async) !== false"
|
||||
dsAutoFocus autoFocusSelector=".suggestion_input"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
></ds-input-suggestions>
|
||||
</div>
|
||||
<small class="text-danger"
|
||||
*ngIf="!(valid | async)">{{"item.edit.metadata.metadatafield.invalid" | translate}}</small>
|
||||
*ngIf="(valid | async) === false">{{"item.edit.metadata.metadatafield.invalid" | translate}}</small>
|
||||
</div>
|
||||
</td>
|
||||
<td class="w-100">
|
||||
|
@@ -11,7 +11,7 @@ import { FieldChangeType } from '../../../../core/data/object-updates/object-upd
|
||||
import { FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer';
|
||||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
||||
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { NgModel } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line:component-selector
|
||||
@@ -27,14 +27,22 @@ 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: Metadatum;
|
||||
|
||||
/**
|
||||
* Emits whether or not this field is currently editable
|
||||
*/
|
||||
@@ -50,11 +58,6 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
|
||||
*/
|
||||
metadataFieldSuggestions: BehaviorSubject<InputSuggestion[]> = new BehaviorSubject([]);
|
||||
|
||||
/**
|
||||
* List of strings with all metadata field keys available
|
||||
*/
|
||||
metadataFields: Observable<string[]>;
|
||||
|
||||
constructor(
|
||||
private metadataFieldService: RegistryService,
|
||||
private objectUpdatesService: ObjectUpdatesService,
|
||||
@@ -67,26 +70,26 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
|
||||
ngOnInit(): void {
|
||||
this.editable = this.objectUpdatesService.isEditable(this.url, this.metadata.uuid);
|
||||
this.valid = this.objectUpdatesService.isValid(this.url, this.metadata.uuid);
|
||||
this.metadataFields = this.findMetadataFields()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a new change update for this field to the object updates service
|
||||
*/
|
||||
update(control?: FormControl) {
|
||||
update(ngModel?: NgModel) {
|
||||
this.objectUpdatesService.saveChangeFieldUpdate(this.url, this.metadata);
|
||||
if (hasValue(control)) {
|
||||
this.checkValidity(control);
|
||||
if (hasValue(ngModel)) {
|
||||
this.checkValidity(ngModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check the validity of a form control
|
||||
* @param control The form control to check
|
||||
* @param ngModel
|
||||
*/
|
||||
private checkValidity(control: FormControl) {
|
||||
control.updateValueAndValidity();
|
||||
this.objectUpdatesService.setValidFieldUpdate(this.url, this.metadata.uuid, control.valid);
|
||||
private checkValidity(ngModel: NgModel) {
|
||||
ngModel.control.setValue(ngModel.viewModel);
|
||||
ngModel.control.updateValueAndValidity();
|
||||
this.objectUpdatesService.setValidFieldUpdate(this.url, this.metadata.uuid, ngModel.control.valid);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,30 +127,24 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
|
||||
* @param query The query to look for
|
||||
*/
|
||||
findMetadataFieldSuggestions(query: string): void {
|
||||
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()
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to request all metadata fields and convert them to a list of strings
|
||||
*/
|
||||
findMetadataFields(): Observable<string[]> {
|
||||
return this.metadataFieldService.getAllMetadataFields().pipe(
|
||||
getSucceededRemoteData(),
|
||||
take(1),
|
||||
map((remoteData$) => remoteData$.payload.page.map((field: MetadataField) => field.toString())));
|
||||
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([]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -33,6 +33,7 @@
|
||||
<tr *ngFor="let updateValue of ((updates$ | async)| dsObjectValues); trackBy: trackUpdate"
|
||||
ds-edit-in-place-field
|
||||
[fieldUpdate]="updateValue || {}"
|
||||
[metadataFields]="metadataFields$ | async"
|
||||
[url]="url"
|
||||
[ngClass]="{
|
||||
'table-warning': updateValue.changeType === 0,
|
||||
|
@@ -11,12 +11,14 @@ import {
|
||||
Identifiable
|
||||
} from '../../../core/data/object-updates/object-updates.reducer';
|
||||
import { Metadatum } from '../../../core/shared/metadatum.model';
|
||||
import { first, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { first, map, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../../config';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { RegistryService } from '../../../core/registry/registry.service';
|
||||
import { MetadataField } from '../../../core/metadata/metadatafield.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-metadata',
|
||||
@@ -47,6 +49,11 @@ export class ItemMetadataComponent implements OnInit {
|
||||
|
||||
private notitifactionPrefix = 'item.edit.metadata.notifications.';
|
||||
|
||||
/**
|
||||
* Observable with a list of strings with all existing metadata field keys
|
||||
*/
|
||||
metadataFields$: Observable<string[]>;
|
||||
|
||||
constructor(
|
||||
private itemService: ItemDataService,
|
||||
private objectUpdatesService: ObjectUpdatesService,
|
||||
@@ -54,7 +61,8 @@ export class ItemMetadataComponent implements OnInit {
|
||||
private notificationsService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||
private route: ActivatedRoute
|
||||
private route: ActivatedRoute,
|
||||
private metadataFieldService: RegistryService,
|
||||
) {
|
||||
|
||||
}
|
||||
@@ -63,6 +71,7 @@ export class ItemMetadataComponent implements OnInit {
|
||||
* Set up and initialize all fields
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.metadataFields$ = this.findMetadataFields()
|
||||
this.route.parent.data.pipe(map((data) => data.item))
|
||||
.pipe(
|
||||
first(),
|
||||
@@ -208,4 +217,14 @@ export class ItemMetadataComponent implements OnInit {
|
||||
return this.translateService.instant(this.notitifactionPrefix + key + '.content');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to request all metadata fields and convert them to a list of strings
|
||||
*/
|
||||
findMetadataFields(): Observable<string[]> {
|
||||
return this.metadataFieldService.getAllMetadataFields().pipe(
|
||||
getSucceededRemoteData(),
|
||||
take(1),
|
||||
map((remoteData$) => remoteData$.payload.page.map((field: MetadataField) => field.toString())));
|
||||
}
|
||||
}
|
||||
|
@@ -78,7 +78,7 @@ const initialFieldState = { editable: false, isNew: false, isValid: true };
|
||||
/**
|
||||
* Initial state for a newly added field
|
||||
*/
|
||||
const initialNewFieldState = { editable: true, isNew: true, isValid: true };
|
||||
const initialNewFieldState = { editable: true, isNew: true, isValid: undefined };
|
||||
|
||||
// Object.create(null) ensures the object has no default js properties (e.g. `__proto__`)
|
||||
const initialState = Object.create(null);
|
||||
|
@@ -2,13 +2,12 @@
|
||||
[action]="action" (keydown)="onKeydown($event)"
|
||||
(keydown.arrowdown)="shiftFocusDown($event)"
|
||||
(keydown.arrowup)="shiftFocusUp($event)" (keydown.esc)="close()"
|
||||
(dsClickOutside)="close()">
|
||||
(dsClickOutside)="close();">
|
||||
<input #inputField type="text" [(ngModel)]="value" [name]="name"
|
||||
class="form-control suggestion_input"
|
||||
[ngClass]="{'is-invalid': !valid}"
|
||||
[dsDebounce]="debounceTime" (onDebounce)="find($event)"
|
||||
[placeholder]="placeholder"
|
||||
(blur)="blur.emit($event);"
|
||||
[ngModelOptions]="{standalone: true}" autocomplete="off"/>
|
||||
<input type="submit" class="d-none"/>
|
||||
<div class="autocomplete dropdown-menu" [ngClass]="{'show': (show | async) && isNotEmpty(suggestions)}">
|
||||
|
@@ -85,11 +85,6 @@ export class InputSuggestionsComponent implements ControlValueAccessor, OnChange
|
||||
*/
|
||||
@Output() findSuggestions = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Emits event when the input field loses focus
|
||||
*/
|
||||
@Output() blur = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Emits true when the list of suggestions should be shown
|
||||
*/
|
||||
@@ -119,9 +114,14 @@ export class InputSuggestionsComponent implements ControlValueAccessor, OnChange
|
||||
*/
|
||||
_value: string;
|
||||
|
||||
/** Fields needed to add ngModel */
|
||||
@Input() disabled = false;
|
||||
propagateChange = (_: any) => {
|
||||
/* Empty implementation */
|
||||
};
|
||||
propagateTouch = (_: any) => {
|
||||
/* Empty implementation */
|
||||
};
|
||||
|
||||
/**
|
||||
* When any of the inputs change, check if we should still show the suggestions
|
||||
@@ -214,9 +214,9 @@ export class InputSuggestionsComponent implements ControlValueAccessor, OnChange
|
||||
find(data) {
|
||||
if (!this.blockReopen) {
|
||||
this.findSuggestions.emit(data);
|
||||
this.typeSuggestion.emit(data);
|
||||
}
|
||||
this.blockReopen = false;
|
||||
this.typeSuggestion.emit(data);
|
||||
}
|
||||
|
||||
onSubmit(data) {
|
||||
@@ -230,11 +230,11 @@ export class InputSuggestionsComponent implements ControlValueAccessor, OnChange
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
/* no implementation */
|
||||
this.propagateTouch = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
/* no implementation */
|
||||
this.disabled = isDisabled;
|
||||
}
|
||||
|
||||
writeValue(value: any): void {
|
||||
|
@@ -14,7 +14,6 @@ export class AutoFocusDirective implements AfterViewInit {
|
||||
ngAfterViewInit() {
|
||||
if (isNotEmpty(this.autoFocusSelector)) {
|
||||
return this.el.nativeElement.querySelector(this.autoFocusSelector).focus();
|
||||
|
||||
} else {
|
||||
return this.el.nativeElement.focus();
|
||||
}
|
||||
|
@@ -16,9 +16,11 @@ export class ClickOutsideDirective {
|
||||
constructor(private _elementRef: ElementRef) {
|
||||
}
|
||||
|
||||
@HostListener('document:click', ['$event.target'])
|
||||
public onClick(targetElement) {
|
||||
const clickedInside = this._elementRef.nativeElement.contains(targetElement);
|
||||
@HostListener('document:click')
|
||||
public onClick() {
|
||||
const hostElement = this._elementRef.nativeElement;
|
||||
const focusElement = hostElement.ownerDocument.activeElement;
|
||||
const clickedInside = hostElement.contains(focusElement);
|
||||
if (!clickedInside) {
|
||||
this.dsClickOutside.emit(null);
|
||||
}
|
||||
|
@@ -25,12 +25,6 @@ export class DebounceDirective implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
public dsDebounce = 500;
|
||||
|
||||
/**
|
||||
* True if no changes have been made to the input field's value
|
||||
*/
|
||||
@Input()
|
||||
private isFirstChange = true;
|
||||
|
||||
/**
|
||||
* Subject to unsubscribe from
|
||||
*/
|
||||
@@ -47,11 +41,9 @@ export class DebounceDirective implements OnInit, OnDestroy {
|
||||
this.model.valueChanges.pipe(
|
||||
takeUntil(this.subject),
|
||||
debounceTime(this.dsDebounce),
|
||||
distinctUntilChanged(),)
|
||||
distinctUntilChanged())
|
||||
.subscribe((modelValue) => {
|
||||
if (this.isFirstChange) {
|
||||
this.isFirstChange = false;
|
||||
} else {
|
||||
if (this.model.dirty) {
|
||||
this.onDebounce.emit(modelValue);
|
||||
}
|
||||
});
|
||||
|
@@ -24,9 +24,6 @@ export class InListValidator implements Validator {
|
||||
* @param c The FormControl
|
||||
*/
|
||||
validate(c: FormControl): ValidationErrors | null {
|
||||
if (this.dsInListValidator !== null) {
|
||||
return inListValidator(this.dsInListValidator)(c);
|
||||
}
|
||||
return null;
|
||||
return inListValidator(this.dsInListValidator)(c);
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,17 @@
|
||||
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
||||
import { isNotEmpty } from '../empty.util';
|
||||
|
||||
/**
|
||||
* Returns a validator function to check if the control's value is in a given list
|
||||
* @param list The list to look in
|
||||
*/
|
||||
export function inListValidator(list: string[]): ValidatorFn {
|
||||
return (control: AbstractControl): {[key: string]: any} | null => {
|
||||
const contains = list.indexOf(control.value) > -1;
|
||||
return contains ? null : {inList: {value: control.value}} };
|
||||
return (control: AbstractControl): { [key: string]: any } | null => {
|
||||
const hasValue = isNotEmpty(control.value);
|
||||
let inList = true;
|
||||
if (isNotEmpty(list)) {
|
||||
inList = list.indexOf(control.value) > -1;
|
||||
}
|
||||
return (hasValue && inList) ? null : { inList: { value: control.value } }
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user