diff --git a/resources/i18n/en.json b/resources/i18n/en.json
index cd7e1276f1..795b3cc747 100644
--- a/resources/i18n/en.json
+++ b/resources/i18n/en.json
@@ -233,6 +233,9 @@
"language": "Lang",
"edit": "Edit"
},
+ "metadatafield": {
+ "invalid": "Please choose a valid metadata field"
+ },
"notifications": {
"outdated": {
"title": "Changed outdated",
@@ -241,6 +244,10 @@
"discarded": {
"title": "Changed discarded",
"content": "Your changes were discarded. To reinstate your changes click the 'Undo' button"
+ },
+ "invalid": {
+ "title": "Metadata invalid",
+ "content": "Please make sure all fields are valid"
}
}
}
diff --git a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html
index 279552565c..51f4c650af 100644
--- a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html
+++ b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html
@@ -13,10 +13,14 @@
[(ngModel)]="metadata.key"
(submitSuggestion)="update()"
(clickSuggestion)="update()"
+ (typeSuggestion)="update()"
(findSuggestions)="findMetadataFieldSuggestions($event)"
+ [formControl]="formControl"
ngDefaultControl
>
+ {{"item.edit.metadata.metadatafield.invalid" | translate}}
@@ -38,10 +42,14 @@
|
-
-
-
-
+
+
+
+
|
\ No newline at end of file
diff --git a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.scss b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.scss
index e69de29bb2..3575cae797 100644
--- a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.scss
+++ b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables.scss';
diff --git a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts
index 38470c54c4..b85b558cfd 100644
--- a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts
+++ b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts
@@ -11,6 +11,9 @@ import { FieldChangeType } from '../../../../core/data/object-updates/object-upd
import { of as observableOf } from 'rxjs';
import { FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
+import { inListValidator } from '../../../../shared/utils/validator.functions';
+import { getSucceededRemoteData } from '../../../../core/shared/operators';
+import { FormControl, ValidationErrors, ValidatorFn } from '@angular/forms';
@Component({
selector: 'ds-edit-in-place-field',
@@ -21,7 +24,6 @@ import { ObjectUpdatesService } from '../../../../core/data/object-updates/objec
* 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
*/
@@ -39,22 +41,43 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
*/
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([]);
+ formControl: FormControl;
+
constructor(
private metadataFieldService: RegistryService,
private objectUpdatesService: ObjectUpdatesService,
) {
}
+ /**
+ * Sets up an observable that keeps track of the current editable and valid state of this field
+ * Also creates a form control object for the input suggestions
+ */
+ ngOnInit(): void {
+ this.editable = this.objectUpdatesService.isEditable(this.route, this.metadata.uuid);
+ this.valid = this.objectUpdatesService.isValid(this.route, this.metadata.uuid);
+ this.findMetadataFields().pipe(take(1)).subscribe((metadataFields: string[]) => {
+ const validator = inListValidator(metadataFields);
+ this.formControl = new FormControl('', validator);
+ });
+ }
+
/**
* Sends a new change update for this field to the object updates service
*/
update() {
this.objectUpdatesService.saveChangeFieldUpdate(this.route, this.metadata);
+ this.objectUpdatesService.setValidFieldUpdate(this.route, this.metadata.uuid, this.formControl.valid);
}
/**
@@ -79,13 +102,6 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
this.objectUpdatesService.removeSingleFieldUpdate(this.route, this.metadata.uuid);
}
- /**
- * Sets up an observable that keeps track of the current editable state of this field
- */
- ngOnInit(): void {
- this.editable = this.objectUpdatesService.isEditable(this.route, this.metadata.uuid);
- }
-
/**
* Sets the current metadatafield based on the fieldUpdate input field
*/
@@ -115,6 +131,13 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
);
}
+ findMetadataFields(): Observable {
+ return this.metadataFieldService.getAllMetadataFields().pipe(
+ getSucceededRemoteData(),
+ take(1),
+ map((remoteData$) => remoteData$.payload.page.map((field: MetadataField) => field.toString())));
+ }
+
/**
* 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
diff --git a/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.scss b/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.scss
new file mode 100644
index 0000000000..b2994dcec7
--- /dev/null
+++ b/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.scss
@@ -0,0 +1,5 @@
+@import '../../../../styles/variables.scss';
+
+.button-row .btn {
+ min-width: $button-min-width;
+}
\ No newline at end of file
diff --git a/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.ts b/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.ts
index d2231e615d..ae32028486 100644
--- a/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.ts
+++ b/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.ts
@@ -11,7 +11,7 @@ import {
Identifiable
} from '../../../core/data/object-updates/object-updates.reducer';
import { Metadatum } from '../../../core/shared/metadatum.model';
-import { first, switchMap, tap } from 'rxjs/operators';
+import { first, map, switchMap, tap } from 'rxjs/operators';
import { getSucceededRemoteData } from '../../../core/shared/operators';
import { RemoteData } from '../../../core/data/remote-data';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
@@ -20,6 +20,7 @@ import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'ds-item-metadata',
+ styleUrls: ['./item-metadata.component.scss'],
templateUrl: './item-metadata.component.html',
})
/**
@@ -114,22 +115,30 @@ export class ItemMetadataComponent implements OnInit {
* Makes sure the new version of the item is rendered on the page
*/
submit() {
- const metadata$: Observable = this.objectUpdatesService.getUpdatedFields(this.route, this.item.metadata) as Observable;
- metadata$.pipe(
- first(),
- switchMap((metadata: Metadatum[]) => {
- const updatedItem: Item = Object.assign(cloneDeep(this.item), { metadata });
- return this.itemService.update(updatedItem);
- }),
- tap(() => this.itemService.commitUpdates()),
- getSucceededRemoteData()
- ).subscribe(
- (rd: RemoteData- ) => {
- this.item = rd.payload;
- this.initializeOriginalFields();
- this.updates$ = this.objectUpdatesService.getFieldUpdates(this.route, this.item.metadata);
- }
- )
+ this.isValid().pipe(first()).subscribe((isValid) => {
+ if (isValid) {
+ const metadata$: Observable = this.objectUpdatesService.getUpdatedFields(this.route, this.item.metadata) as Observable;
+ metadata$.pipe(
+ first(),
+ switchMap((metadata: Metadatum[]) => {
+ const updatedItem: Item = Object.assign(cloneDeep(this.item), { metadata });
+ return this.itemService.update(updatedItem);
+ }),
+ tap(() => this.itemService.commitUpdates()),
+ getSucceededRemoteData()
+ ).subscribe(
+ (rd: RemoteData
- ) => {
+ this.item = rd.payload;
+ this.initializeOriginalFields();
+ this.updates$ = this.objectUpdatesService.getFieldUpdates(this.route, this.item.metadata);
+ }
+ )
+ } else {
+ const title = this.translateService.instant('item.edit.metadata.notifications.invalid.title');
+ const content = this.translateService.instant('item.edit.metadata.notifications.invalid.content');
+ this.notificationsService.error(title, content);
+ }
+ });
}
/**
@@ -163,4 +172,8 @@ export class ItemMetadataComponent implements OnInit {
}
);
}
+
+ private isValid() {
+ return this.objectUpdatesService.isValidPage(this.route);
+ }
}
diff --git a/src/app/+item-page/item-page-routing.module.ts b/src/app/+item-page/item-page-routing.module.ts
index 1ce4f3ad88..0f2f0817f6 100644
--- a/src/app/+item-page/item-page-routing.module.ts
+++ b/src/app/+item-page/item-page-routing.module.ts
@@ -38,7 +38,7 @@ const ITEM_EDIT_PATH = ':id/edit';
{
path: ITEM_EDIT_PATH,
loadChildren: './edit-item-page/edit-item-page.module#EditItemPageModule',
- canActivate: [AuthenticatedGuard]
+ // canActivate: [AuthenticatedGuard]
}
])
],
diff --git a/src/app/core/core.reducers.ts b/src/app/core/core.reducers.ts
index 760d3ddeaf..e0ddb4a9de 100644
--- a/src/app/core/core.reducers.ts
+++ b/src/app/core/core.reducers.ts
@@ -1,8 +1,6 @@
import {
ActionReducerMap,
createFeatureSelector,
- createSelector,
- MemoizedSelector
} from '@ngrx/store';
import { objectCacheReducer, ObjectCacheState } from './cache/object-cache.reducer';
@@ -14,8 +12,6 @@ import {
objectUpdatesReducer,
ObjectUpdatesState
} from './data/object-updates/object-updates.reducer';
-import { hasValue } from '../shared/empty.util';
-import { AppState } from '../app.reducer';
export interface CoreState {
'cache/object': ObjectCacheState,
@@ -35,4 +31,4 @@ export const coreReducers: ActionReducerMap = {
'auth': authReducer,
};
-export const coreSelector = createFeatureSelector('core');
\ No newline at end of file
+export const coreSelector = createFeatureSelector('core');
diff --git a/src/app/core/data/object-updates/object-updates.actions.ts b/src/app/core/data/object-updates/object-updates.actions.ts
index 5f76d6dde9..c1b35d07b4 100644
--- a/src/app/core/data/object-updates/object-updates.actions.ts
+++ b/src/app/core/data/object-updates/object-updates.actions.ts
@@ -9,6 +9,7 @@ import { INotification } from '../../../shared/notifications/models/notification
export const ObjectUpdatesActionTypes = {
INITIALIZE_FIELDS: type('dspace/core/cache/object-updates/INITIALIZE_FIELDS'),
SET_EDITABLE_FIELD: type('dspace/core/cache/object-updates/SET_EDITABLE_FIELD'),
+ SET_VALID_FIELD: type('dspace/core/cache/object-updates/SET_VALID_FIELD'),
ADD_FIELD: type('dspace/core/cache/object-updates/ADD_FIELD'),
DISCARD: type('dspace/core/cache/object-updates/DISCARD'),
REINSTATE: type('dspace/core/cache/object-updates/REINSTATE'),
@@ -109,6 +110,33 @@ export class SetEditableFieldUpdateAction implements Action {
}
}
+/**
+ * An ngrx action to set the isValid state of an existing field in the ObjectUpdates state for a certain page url
+ */
+export class SetValidFieldUpdateAction implements Action {
+ type = ObjectUpdatesActionTypes.SET_VALID_FIELD;
+ payload: {
+ url: string,
+ uuid: string,
+ isValid: boolean,
+ };
+
+ /**
+ * Create a new SetEditableFieldUpdateAction
+ *
+ * @param url
+ * the unique url of the page
+ * @param fieldUUID The UUID of the field of which
+ * @param isValid The new isValid value for the field
+ */
+ constructor(
+ url: string,
+ fieldUUID: string,
+ isValid: boolean) {
+ this.payload = { url, uuid: fieldUUID, isValid };
+ }
+}
+
/**
* An ngrx action to discard all existing updates in the ObjectUpdates state for a certain page url
*/
diff --git a/src/app/core/data/object-updates/object-updates.reducer.ts b/src/app/core/data/object-updates/object-updates.reducer.ts
index c46438bd90..dad394e84f 100644
--- a/src/app/core/data/object-updates/object-updates.reducer.ts
+++ b/src/app/core/data/object-updates/object-updates.reducer.ts
@@ -7,7 +7,7 @@ import {
ObjectUpdatesActionTypes,
ReinstateObjectUpdatesAction,
RemoveFieldUpdateAction,
- RemoveObjectUpdatesAction, SetEditableFieldUpdateAction
+ RemoveObjectUpdatesAction, SetEditableFieldUpdateAction, SetValidFieldUpdateAction
} from './object-updates.actions';
import { hasNoValue, hasValue } from '../../../shared/empty.util';
@@ -15,7 +15,8 @@ export const OBJECT_UPDATES_TRASH_PATH = '/trash';
export interface FieldState {
editable: boolean,
- isNew: boolean
+ isNew: boolean,
+ isValid: boolean
}
export interface FieldStates {
@@ -46,8 +47,8 @@ export interface ObjectUpdatesState {
[url: string]: ObjectUpdatesEntry;
}
-const initialFieldState = { editable: false, isNew: false };
-const initialNewFieldState = { editable: true, isNew: true };
+const initialFieldState = { editable: false, isNew: false, isValid: true };
+const initialNewFieldState = { editable: true, isNew: true, isValid: true };
// Object.create(null) ensures the object has no default js properties (e.g. `__proto__`)
const initialState = Object.create(null);
@@ -80,6 +81,9 @@ export function objectUpdatesReducer(state = initialState, action: ObjectUpdates
case ObjectUpdatesActionTypes.SET_EDITABLE_FIELD: {
return setEditableFieldUpdate(state, action as SetEditableFieldUpdateAction);
}
+ case ObjectUpdatesActionTypes.SET_VALID_FIELD: {
+ return setValidFieldUpdate(state, action as SetValidFieldUpdateAction);
+ }
default: {
return state;
}
@@ -147,8 +151,8 @@ function discardObjectUpdates(state: any, action: DiscardObjectUpdatesAction) {
Object.keys(pageState.fieldStates).forEach((uuid: string) => {
const fieldState: FieldState = pageState.fieldStates[uuid];
if (!fieldState.isNew) {
- /* After discarding we don't want the reset fields to stay editable */
- newFieldStates[uuid] = Object.assign({}, fieldState, { editable: false });
+ /* After discarding we don't want the reset fields to stay editable or invalid */
+ newFieldStates[uuid] = Object.assign({}, fieldState, { editable: false, isValid: true });
}
});
@@ -215,7 +219,7 @@ function removeFieldUpdate(state: any, action: RemoveFieldUpdateAction) {
/* If this field was added, just throw it away */
delete newFieldStates[uuid];
} else {
- newFieldStates[uuid] = Object.assign({}, newFieldStates[uuid], { editable: false });
+ newFieldStates[uuid] = Object.assign({}, newFieldStates[uuid], { editable: false, isValid: true });
}
}
newPageState = Object.assign({}, state[url], {
@@ -243,7 +247,7 @@ function determineChangeType(oldType: FieldChangeType, newType: FieldChangeType)
}
/**
- * Set the state of a specific action's url and uuid to false or true
+ * Set the editable state of a specific action's url and uuid to false or true
* @param state The current state
* @param action The action to perform on the current state
*/
@@ -264,6 +268,28 @@ function setEditableFieldUpdate(state: any, action: SetEditableFieldUpdateAction
return Object.assign({}, state, { [url]: newPageState });
}
+/**
+ * Set the isValid state of a specific action's url and uuid to false or true
+ * @param state The current state
+ * @param action The action to perform on the current state
+ */
+function setValidFieldUpdate(state: any, action: SetValidFieldUpdateAction) {
+ const url: string = action.payload.url;
+ const uuid: string = action.payload.uuid;
+ const isValid: boolean = action.payload.isValid;
+
+ const pageState: ObjectUpdatesEntry = state[url];
+
+ const fieldState = pageState.fieldStates[uuid];
+ const newFieldState = Object.assign({}, fieldState, { isValid });
+
+ const newFieldStates = Object.assign({}, pageState.fieldStates, { [uuid]: newFieldState });
+
+ const newPageState = Object.assign({}, pageState, { fieldStates: newFieldStates });
+
+ return Object.assign({}, state, { [url]: newPageState });
+}
+
/**
* Method to create an initial FieldStates object based on a list of Identifiable objects
* @param fields Identifiable objects
diff --git a/src/app/core/data/object-updates/object-updates.service.ts b/src/app/core/data/object-updates/object-updates.service.ts
index 4b6c7def0d..6136d31ac0 100644
--- a/src/app/core/data/object-updates/object-updates.service.ts
+++ b/src/app/core/data/object-updates/object-updates.service.ts
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
import { coreSelector, CoreState } from '../../core.reducers';
import {
+ FieldState,
FieldUpdates,
Identifiable, OBJECT_UPDATES_TRASH_PATH,
ObjectUpdatesEntry,
@@ -15,7 +16,7 @@ import {
InitializeFieldsAction,
ReinstateObjectUpdatesAction,
RemoveFieldUpdateAction,
- SetEditableFieldUpdateAction
+ SetEditableFieldUpdateAction, SetValidFieldUpdateAction
} from './object-updates.actions';
import { filter, map } from 'rxjs/operators';
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
@@ -102,6 +103,33 @@ export class ObjectUpdatesService {
)
}
+ /**
+ * Method to check if a specific field is currently valid in the store
+ * @param url The URL of the page on which the field resides
+ * @param uuid The UUID of the field
+ */
+ isValid(url: string, uuid: string): Observable {
+ const objectUpdates = this.getObjectEntry(url);
+ return objectUpdates.pipe(
+ filter((objectEntry) => hasValue(objectEntry.fieldStates[uuid])),
+ map((objectEntry) => objectEntry.fieldStates[uuid].isValid
+ )
+ )
+ }
+
+ /**
+ * Method to check if a specific page is currently valid in the store
+ * @param url The URL of the page
+ */
+ isValidPage(url: string): Observable {
+ const objectUpdates = this.getObjectEntry(url);
+ return objectUpdates.pipe(
+ map((entry: ObjectUpdatesEntry) => {
+ return Object.values(entry.fieldStates).findIndex((state: FieldState) => !state.isValid) < 0
+ })
+ )
+ }
+
/**
* Calls the saveFieldUpdate method with FieldChangeType.ADD
* @param url The page's URL for which the changes are saved
@@ -139,6 +167,16 @@ export class ObjectUpdatesService {
this.store.dispatch(new SetEditableFieldUpdateAction(url, uuid, editable));
}
+ /**
+ * Dispatches a SetValidFieldUpdateAction to the store to set a field's isValid state
+ * @param url The URL of the page on which the field resides
+ * @param uuid The UUID of the field that should be set
+ * @param valid The new value of isValid in the store for this field
+ */
+ setValidFieldUpdate(url: string, uuid: string, valid: boolean) {
+ this.store.dispatch(new SetValidFieldUpdateAction(url, uuid, valid));
+ }
+
/**
* Method to dispatch an DiscardObjectUpdatesAction to the store
* @param url The page's URL for which the changes should be discarded
diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.html b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.html
index 720ad0c1cf..6c67937063 100644
--- a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.html
+++ b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.html
@@ -1,3 +1,3 @@
+ [formModel]="formModel" (submitForm)="onSubmit()" (cancel)="onCancel()">
diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts
index a6f5e0d45a..51c5928348 100644
--- a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts
+++ b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts
@@ -50,10 +50,7 @@ describe('ComColFormComponent', () => {
];
/* tslint:disable:no-empty */
- const locationStub = {
- back: () => {
- }
- };
+ const locationStub = jasmine.createSpyObj('location', ['back']);
/* tslint:enable:no-empty */
beforeEach(async(() => {
@@ -112,4 +109,11 @@ describe('ComColFormComponent', () => {
);
})
});
+
+ describe('onCancel', () => {
+ it('should call the back method on the Location service', () => {
+ comp.onCancel();
+ expect(locationStub.back).toHaveBeenCalled();
+ });
+ });
});
diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts
index 17710fd1c6..a7d638e791 100644
--- a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts
+++ b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts
@@ -112,4 +112,8 @@ export class ComColFormComponent implements OnInit {
}
);
}
+
+ onCancel() {
+ this.location.back();
+ }
}
diff --git a/src/app/shared/input-suggestions/input-suggestions.component.ts b/src/app/shared/input-suggestions/input-suggestions.component.ts
index 434a285818..afb405a60a 100644
--- a/src/app/shared/input-suggestions/input-suggestions.component.ts
+++ b/src/app/shared/input-suggestions/input-suggestions.component.ts
@@ -1,9 +1,13 @@
import {
Component,
- ElementRef, EventEmitter, forwardRef,
+ ElementRef,
+ EventEmitter,
+ forwardRef,
Input,
+ OnChanges,
Output,
- QueryList, SimpleChanges,
+ QueryList,
+ SimpleChanges,
ViewChild,
ViewChildren
} from '@angular/core';
@@ -19,6 +23,8 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
providers: [
{
provide: NG_VALUE_ACCESSOR,
+ // Usage of forwardRef necessary https://github.com/angular/angular.io/issues/1151
+ // tslint:disable-next-line:no-forward-ref
useExisting: forwardRef(() => InputSuggestionsComponent),
multi: true
}
@@ -28,7 +34,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
/**
* Component representing a form with a autocomplete functionality
*/
-export class InputSuggestionsComponent implements ControlValueAccessor {
+export class InputSuggestionsComponent implements ControlValueAccessor, OnChanges {
/**
* The suggestions that should be shown
*/
@@ -64,6 +70,11 @@ export class InputSuggestionsComponent implements ControlValueAccessor {
*/
@Output() clickSuggestion = new EventEmitter();
+ /**
+ * Output for when something is typed in the input field
+ */
+ @Output() typeSuggestion = new EventEmitter();
+
/**
* Output for when new suggestions should be requested
*/
@@ -195,6 +206,7 @@ export class InputSuggestionsComponent implements ControlValueAccessor {
this.findSuggestions.emit(data);
}
this.blockReopen = false;
+ this.typeSuggestion.emit(data);
}
onSubmit(data) {
diff --git a/src/app/shared/utils/validator.functions.ts b/src/app/shared/utils/validator.functions.ts
new file mode 100644
index 0000000000..55fe498747
--- /dev/null
+++ b/src/app/shared/utils/validator.functions.ts
@@ -0,0 +1,7 @@
+import { AbstractControl, ValidatorFn } from '@angular/forms';
+
+export function inListValidator(list: string[]): ValidatorFn {
+ return (control: AbstractControl): {[key: string]: any} | null => {
+ const contains = list.indexOf(control.value) > 0;
+ return contains ? null : {inList: {value: control.value}} };
+}
diff --git a/src/styles/_custom_variables.scss b/src/styles/_custom_variables.scss
index e8d839826d..dda018ad2c 100644
--- a/src/styles/_custom_variables.scss
+++ b/src/styles/_custom_variables.scss
@@ -1,6 +1,8 @@
$content-spacing: $spacer * 1.5;
$button-height: $input-btn-padding-y * 2 + $input-btn-line-height + calculateRem($input-btn-border-width*2);
+$button-min-width: 100px;
+
$card-height-percentage:98%;
$card-thumbnail-height:240px;
$dropdown-menu-max-height: 200px;