You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2020/06/12 19:51:18 UTC
[airavata-django-portal] 05/05: AIRAVATA-3285 Validation for
interactive parameters
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch AIRAVATA-3285--Interactive-output-view-providers
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit f6a8865cabcdb8d2be998486df484af5071ce49c
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Fri Jun 12 15:50:43 2020 -0400
AIRAVATA-3285 Validation for interactive parameters
---
.../output-displays/OutputDisplayContainer.vue | 11 +++
.../InteractiveParameterStepperWidget.vue | 22 +++++-
.../InteractiveParameterWidgetContainer.vue | 2 +
.../InteractiveParametersPanel.vue | 51 ++++++++++----
.../static/common/js/components/ValidatedForm.vue | 82 ++++++++++++++++++++++
.../common/js/components/ValidatedFormGroup.vue | 43 ++++++++++++
django_airavata/static/common/js/index.js | 6 +-
7 files changed, 200 insertions(+), 17 deletions(-)
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/OutputDisplayContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/OutputDisplayContainer.vue
index 4cd9384..b7d42f3 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/OutputDisplayContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/OutputDisplayContainer.vue
@@ -19,6 +19,7 @@
:experiment-output="experimentOutput"
/>
<interactive-parameters-panel
+ ref="interactiveParametersPanel"
v-if="viewData && viewData.interactive"
:parameters="viewData.interactive"
@input="parametersUpdated"
@@ -134,6 +135,9 @@ export default {
},
providerId() {
return this.currentView["provider-id"];
+ },
+ hasInteractiveParameters() {
+ return this.viewData && this.viewData.interactive;
}
},
methods: {
@@ -147,6 +151,13 @@ export default {
}
},
parametersUpdated(newParams) {
+ if (
+ this.hasInteractiveParameters &&
+ !this.$refs.interactiveParametersPanel.valid
+ ) {
+ // Don't update if we have invalid interactive parameters
+ return;
+ }
this.loader.load(newParams);
},
createLoader() {
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue
index a048f1e..d136124 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue
@@ -32,20 +32,36 @@ export default {
},
data() {
return {
- currentValue: parseFloat(this.value)
+ currentValue: parseFloat(this.value),
+ valid: false
};
},
computed: {
disabled() {
- return this.currentValue === parseFloat(this.value);
+ return !this.valid || this.currentValue === parseFloat(this.value);
}
},
methods: {
updateValue(newValue) {
+ if ("max" in this.parameter) {
+ newValue = Math.min(this.parameter.max, newValue);
+ }
+ if ("min" in this.parameter) {
+ newValue = Math.max(this.parameter.min, newValue);
+ }
this.currentValue = parseFloat(newValue);
+ if (this.$refs.textInput.validity.valid) {
+ this.valid = true;
+ this.$emit("valid");
+ } else {
+ this.valid = false;
+ this.$emit("invalid", this.$refs.textInput.validationMessage);
+ }
},
submit() {
- this.$emit("input", this.currentValue);
+ if (!this.disabled) {
+ this.$emit("input", this.currentValue);
+ }
},
enterKeyPressed() {
if (!this.disabled) {
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterWidgetContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterWidgetContainer.vue
index c9d21c1..33ada99 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterWidgetContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterWidgetContainer.vue
@@ -4,6 +4,8 @@
:value="parameter.value"
:parameter="parameter"
@input="$emit('input', $event)"
+ @valid="$emit('valid')"
+ @invalid="$emit('invalid', $event)"
/>
</template>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParametersPanel.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParametersPanel.vue
index aa2018d..6ad640d 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParametersPanel.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParametersPanel.vue
@@ -1,24 +1,26 @@
<template>
<b-card title="Parameters">
- <b-form-group
- v-for="param in parameters"
- :key="param.name"
- :label="param.name"
- >
+ <validated-form ref="validatedForm" :items="formItems">
<interactive-parameter-widget-container
- :parameter="param"
- @input="updated(param, $event)"/>
- </b-form-group>
+ slot-scope="form"
+ :parameter="form.item"
+ @valid="form.valid"
+ @invalid="form.invalid"
+ @input="updated(form.item, $event)"
+ />
+ </validated-form>
</b-card>
</template>
<script>
import InteractiveParameterWidgetContainer from "./InteractiveParameterWidgetContainer";
+import { components } from "django-airavata-common-ui";
export default {
name: "interactive-parameters-panel",
components: {
- InteractiveParameterWidgetContainer
+ InteractiveParameterWidgetContainer,
+ "validated-form": components.ValidatedForm
},
props: {
parameters: {
@@ -26,16 +28,39 @@ export default {
required: true
}
},
+ computed: {
+ formItems() {
+ return this.localParameters.map(p => {
+ return {
+ key: p.name,
+ label: p.name,
+ item: p
+ };
+ });
+ },
+ valid() {
+ return this.$refs.validatedForm.valid;
+ }
+ },
+ data() {
+ return {
+ localParameters: this.parametersCopy()
+ }
+ },
methods: {
updated(param, value) {
- const params = this.parametersCopy();
- const i = params.findIndex(x => x.name === param.name);
- params[i].value = value;
- this.$emit("input", params);
+ const i = this.localParameters.findIndex(x => x.name === param.name);
+ this.localParameters[i].value = value;
+ this.$emit("input", this.localParameters);
},
parametersCopy() {
return JSON.parse(JSON.stringify(this.parameters));
}
+ },
+ watch: {
+ parameters() {
+ this.localParameters = this.parametersCopy();
+ }
}
};
</script>
diff --git a/django_airavata/static/common/js/components/ValidatedForm.vue b/django_airavata/static/common/js/components/ValidatedForm.vue
new file mode 100644
index 0000000..a9d7608
--- /dev/null
+++ b/django_airavata/static/common/js/components/ValidatedForm.vue
@@ -0,0 +1,82 @@
+<template>
+ <b-form>
+ <template v-for="item in items">
+ <validated-form-group
+ :label="item.label"
+ :key="item.key"
+ :valid="isValid(item.key)"
+ :feedback-messages="getFeedbackMessages(item.key)"
+ >
+ <slot
+ :item="item.item"
+ :valid="() => setValid(item.key)"
+ :invalid="messages => setInvalid(item.key, messages)"
+ />
+ </validated-form-group>
+ </template>
+ </b-form>
+</template>
+
+<script>
+import ValidatedFormGroup from "./ValidatedFormGroup";
+
+export default {
+ name: "validated-form",
+ props: {
+ items: {
+ type: Array,
+ required: true
+ }
+ },
+ components: {
+ ValidatedFormGroup
+ },
+ data() {
+ return {
+ invalidFormItems: [],
+ feedbackMessages: {}
+ };
+ },
+ computed: {
+ valid() {
+ return this.invalidFormItems.length === 0;
+ }
+ },
+ methods: {
+ setValid(key) {
+ const wasValid = this.valid;
+ if (this.invalidFormItems.includes(key)) {
+ const index = this.invalidFormItems.indexOf(key);
+ this.invalidFormItems.splice(index, 1);
+ }
+ if (!wasValid && this.valid) {
+ this.$emit('valid');
+ }
+ },
+ setInvalid(key, messages) {
+ const wasValid = this.valid;
+ if (!this.invalidFormItems.includes(key)) {
+ this.invalidFormItems.push(key);
+ }
+ if (typeof messages === "string") {
+ this.feedbackMessages[key] = [messages];
+ } else {
+ this.feedbackMessages[key] = messages;
+ }
+ if (wasValid) {
+ this.$emit('invalid');
+ }
+ },
+ isValid(key) {
+ return !this.invalidFormItems.includes(key);
+ },
+ getFeedbackMessages(key) {
+ if (key in this.feedbackMessages) {
+ return this.feedbackMessages[key];
+ } else {
+ return [];
+ }
+ }
+ }
+};
+</script>
diff --git a/django_airavata/static/common/js/components/ValidatedFormGroup.vue b/django_airavata/static/common/js/components/ValidatedFormGroup.vue
new file mode 100644
index 0000000..6cb2861
--- /dev/null
+++ b/django_airavata/static/common/js/components/ValidatedFormGroup.vue
@@ -0,0 +1,43 @@
+<template>
+ <b-form-group :label="label" :state="state" :description="description">
+ <slot></slot>
+ <template slot="invalid-feedback">
+ <ul v-if="feedbackMessages && feedbackMessages.length > 1">
+ <li v-for="feedback in feedbackMessages" :key="feedback">
+ {{ feedback }}
+ </li>
+ </ul>
+ <div v-else-if="feedbackMessages && feedbackMessages.length === 1">
+ {{ feedbackMessages[0] }}
+ </div>
+ </template>
+ </b-form-group>
+</template>
+
+<script>
+export default {
+ name: "validated-form-group",
+ props: {
+ label: {
+ type: String,
+ required: true
+ },
+ description: {
+ type: String
+ },
+ valid: {
+ type: Boolean,
+ required: true
+ },
+ feedbackMessages: {
+ type: Array,
+ required: true
+ }
+ },
+ computed: {
+ state() {
+ return this.valid ? null : "invalid";
+ }
+ }
+};
+</script>
diff --git a/django_airavata/static/common/js/index.js b/django_airavata/static/common/js/index.js
index 25b3519..87ae172 100644
--- a/django_airavata/static/common/js/index.js
+++ b/django_airavata/static/common/js/index.js
@@ -20,6 +20,8 @@ import SidebarFeed from "./components/SidebarFeed.vue";
import SidebarHeader from "./components/SidebarHeader.vue";
import UnsavedChangesGuard from "./components/UnsavedChangesGuard.vue";
import Uppy from "./components/Uppy";
+import ValidatedForm from "./components/ValidatedForm";
+import ValidatedFormGroup from "./components/ValidatedFormGroup";
import GlobalErrorHandler from "./errors/GlobalErrorHandler";
import ValidationErrors from "./errors/ValidationErrors";
@@ -57,7 +59,9 @@ const components = {
SidebarFeed,
SidebarHeader,
UnsavedChangesGuard,
- Uppy
+ Uppy,
+ ValidatedForm,
+ ValidatedFormGroup,
};
const errors = {