diff --git a/src/app/process-page/form/process-form.component.html b/src/app/process-page/form/process-form.component.html
index 3bb6a796c6c..5cc10b63ac1 100644
--- a/src/app/process-page/form/process-form.component.html
+++ b/src/app/process-page/form/process-form.component.html
@@ -5,10 +5,10 @@
diff --git a/src/app/process-page/form/process-form.component.spec.ts b/src/app/process-page/form/process-form.component.spec.ts
index 9d84f2774ca..c4acafd3e15 100644
--- a/src/app/process-page/form/process-form.component.spec.ts
+++ b/src/app/process-page/form/process-form.component.spec.ts
@@ -110,6 +110,31 @@ describe('ProcessFormComponent', () => {
expect(scriptService.invoke).toHaveBeenCalled();
});
+ it('should mark the form as submitted when submit is attempted', () => {
+ expect(component.submitted).toBeFalse();
+ component.submitForm({ controls: {} } as any);
+ expect(component.submitted).toBeTrue();
+ });
+
+ describe('when no script is selected', () => {
+ beforeEach(() => {
+ component.selectedScript = undefined;
+ });
+
+ it('should not invoke the script on submit', () => {
+ component.submitForm({ controls: {} } as any);
+ expect(scriptService.invoke).not.toHaveBeenCalled();
+ });
+
+ it('should report that a script is not selected', () => {
+ expect(component.isScriptSelected).toBeFalse();
+ });
+ });
+
+ it('should report that a script is selected', () => {
+ expect(component.isScriptSelected).toBeTrue();
+ });
+
describe('when undefined parameters are provided', () => {
beforeEach(() => {
component.parameters = undefined;
diff --git a/src/app/process-page/form/process-form.component.ts b/src/app/process-page/form/process-form.component.ts
index 72f9e08d4a2..3dfcb8b2f01 100644
--- a/src/app/process-page/form/process-form.component.ts
+++ b/src/app/process-page/form/process-form.component.ts
@@ -21,12 +21,16 @@ import { ProcessParameter } from '@dspace/core/processes/process-parameter.model
import { getFirstCompletedRemoteData } from '@dspace/core/shared/operators';
import { Script } from '@dspace/core/shared/scripts/script.model';
import { ScriptParameter } from '@dspace/core/shared/scripts/script-parameter.model';
-import { isEmpty } from '@dspace/shared/utils/empty.util';
+import {
+ hasValue,
+ isEmpty,
+} from '@dspace/shared/utils/empty.util';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
+import { BtnDisabledDirective } from '../../shared/btn-disabled.directive';
import { getProcessListRoute } from '../process-page-routing.paths';
import { ProcessParametersComponent } from './process-parameters/process-parameters.component';
import { ScriptHelpComponent } from './script-help/script-help.component';
@@ -40,6 +44,7 @@ import { ScriptsSelectComponent } from './scripts-select/scripts-select.componen
templateUrl: './process-form.component.html',
styleUrls: ['./process-form.component.scss'],
imports: [
+ BtnDisabledDirective,
FormsModule,
ProcessParametersComponent,
RouterLink,
@@ -79,6 +84,19 @@ export class ProcessFormComponent implements OnInit {
*/
public missingParameters = [];
+ /**
+ * Indicates whether the form has been submitted
+ * Used to surface validation errors on an interrupted submission
+ */
+ public submitted = false;
+
+ /**
+ * Indicates whether a script has been selected
+ */
+ get isScriptSelected(): boolean {
+ return hasValue(this.selectedScript);
+ }
+
constructor(
private scriptService: ScriptDataService,
private notificationsService: NotificationsService,
@@ -95,10 +113,11 @@ export class ProcessFormComponent implements OnInit {
* @param form
*/
submitForm(form: NgForm) {
+ this.submitted = true;
if (isEmpty(this.parameters)) {
this.parameters = [];
}
- if (!this.validateForm(form) || this.isRequiredMissing()) {
+ if (!this.isScriptSelected || !this.validateForm(form) || this.isRequiredMissing()) {
return;
}
@@ -157,6 +176,9 @@ export class ProcessFormComponent implements OnInit {
private isRequiredMissing() {
this.missingParameters = [];
+ if (!this.isScriptSelected || isEmpty(this.selectedScript.parameters)) {
+ return false;
+ }
const setParams: string[] = this.parameters
.map((param) => param.name);
const requiredParams: ScriptParameter[] = this.selectedScript.parameters.filter((param) => param.mandatory);
diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.html b/src/app/process-page/form/scripts-select/scripts-select.component.html
index 2954e962716..86965046e63 100644
--- a/src/app/process-page/form/scripts-select/scripts-select.component.html
+++ b/src/app/process-page/form/scripts-select/scripts-select.component.html
@@ -39,7 +39,7 @@
- @if (script.invalid && (script.dirty || script.touched)) {
+ @if (script.invalid && (script.dirty || script.touched || submitted)) {
@if (script.errors.required) {
diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts b/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts
index 2765edf3a11..191f1b19a10 100644
--- a/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts
+++ b/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts
@@ -110,6 +110,32 @@ describe('ScriptsSelectComponent', () => {
expect(validationError).toBeFalsy();
}));
+ it('should show a validation error if the form was submitted but the input was left empty', fakeAsync(() => {
+ component.submitted = true;
+ fixture.detectChanges();
+ tick();
+
+ const validationError = fixture.debugElement.query(By.css('.validation-error'));
+ expect(validationError).toBeTruthy();
+ }));
+
+ it('should not show a validation error if the form was submitted but the input was not left empty', fakeAsync(() => {
+ (component as any)._selectedScript.id = 'testValue';
+ fixture.detectChanges();
+ tick();
+
+ const select = fixture.debugElement.query(By.css('#process-script'));
+ select.triggerEventHandler('blur', null);
+ fixture.detectChanges();
+
+ component.submitted = true;
+ fixture.detectChanges();
+ tick();
+
+ const validationError = fixture.debugElement.query(By.css('.validation-error'));
+ expect(validationError).toBeFalsy();
+ }));
+
it('should load more scripts when scrolled to the bottom', fakeAsync(() => {
spyOn(component, 'loadScripts');
const event = {
diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.ts b/src/app/process-page/form/scripts-select/scripts-select.component.ts
index a3564e520aa..205503fbc22 100644
--- a/src/app/process-page/form/scripts-select/scripts-select.component.ts
+++ b/src/app/process-page/form/scripts-select/scripts-select.component.ts
@@ -67,6 +67,11 @@ export class ScriptsSelectComponent implements OnInit, OnDestroy {
* Emits the selected script when the selection changes
*/
@Output() select: EventEmitter