diff --git a/app/client/src/components/formControls/BaseControl.tsx b/app/client/src/components/formControls/BaseControl.tsx
index d579055b7c..0d554b236b 100644
--- a/app/client/src/components/formControls/BaseControl.tsx
+++ b/app/client/src/components/formControls/BaseControl.tsx
@@ -1,10 +1,7 @@
import { Component } from "react";
import { ControlType } from "constants/PropertyControlConstants";
import { InputType } from "components/constants";
-import {
- ConditonalObject,
- DynamicValues,
-} from "reducers/evaluationReducers/formEvaluationReducer";
+import { ConditonalObject } from "reducers/evaluationReducers/formEvaluationReducer";
import { DropdownOption } from "components/ads/Dropdown";
// eslint-disable-next-line @typescript-eslint/ban-types
abstract class BaseControl
extends Component<
@@ -77,7 +74,6 @@ export interface ControlData {
identifier?: string;
sectionName?: string;
disabled?: boolean;
- dynamicFetchedValues?: DynamicValues; // Object that holds the output of the dynamic fetched values
}
export type FormConfig = Omit & {
configProperty?: string;
diff --git a/app/client/src/components/formControls/DropDownControl.tsx b/app/client/src/components/formControls/DropDownControl.tsx
index 2e2afd5643..124c10177d 100644
--- a/app/client/src/components/formControls/DropDownControl.tsx
+++ b/app/client/src/components/formControls/DropDownControl.tsx
@@ -9,7 +9,9 @@ import {
WrappedFieldInputProps,
WrappedFieldMetaProps,
} from "redux-form";
-import { DynamicValues } from "reducers/evaluationReducers/formEvaluationReducer";
+import { connect } from "react-redux";
+import { AppState } from "reducers";
+import { getDynamicFetchedValues } from "selectors/formSelectors";
const DropdownSelect = styled.div`
font-size: 14px;
@@ -27,23 +29,12 @@ class DropDownControl extends BaseControl {
width = this.props.customStyles.width;
}
- // Options will be set dynamically if the config has fetchOptionsConditionally set to true
- let options = this.props.options;
- let isLoading = false;
- if (
- this.props.fetchOptionsCondtionally &&
- !!this.props.dynamicFetchedValues
- ) {
- options = this.props.dynamicFetchedValues.data;
- isLoading = this.props.dynamicFetchedValues.isLoading;
- }
-
return (
@@ -55,39 +46,38 @@ class DropDownControl extends BaseControl {
}
}
-function renderDropdown(props: {
- input?: WrappedFieldInputProps;
- meta?: WrappedFieldMetaProps;
- props: DropDownControlProps;
- width: string;
- formName: string;
- isLoading?: boolean;
- options: DropdownOption[];
- disabled?: boolean;
-}): JSX.Element {
+function renderDropdown(
+ props: {
+ input?: WrappedFieldInputProps;
+ meta?: Partial;
+ width: string;
+ } & DropDownControlProps,
+): JSX.Element {
let selectedValue = props.input?.value;
if (_.isUndefined(props.input?.value)) {
- selectedValue = props?.props?.initialValue;
+ selectedValue = props?.initialValue;
+ }
+ let options: DropdownOption[] = [];
+ let selectedOption = {};
+ if (typeof props.options === "object" && Array.isArray(props.options)) {
+ options = props.options;
+ selectedOption =
+ options.find(
+ (option: DropdownOption) => option.value === selectedValue,
+ ) || {};
}
-
- const selectedOption =
- props.options.find(
- (option: DropdownOption) => option.value === selectedValue,
- ) || {};
return (
{
+ // Added default options to prevent error when options is undefined
+ let isLoading = false;
+ let options: DropdownOption[] = ownProps.fetchOptionsCondtionally
+ ? []
+ : ownProps.options;
+
+ try {
+ if (ownProps.fetchOptionsCondtionally) {
+ const dynamicFetchedValues = getDynamicFetchedValues(
+ state,
+ ownProps.configProperty,
+ );
+ isLoading = dynamicFetchedValues.isLoading;
+ options = dynamicFetchedValues.data;
+ }
+ return { isLoading, options };
+ } catch (e) {
+ return {
+ isLoading,
+ options,
+ };
+ }
+};
+
+// Connecting this componenet to the state to allow for dynamic fetching of options to be updated.
+export default connect(mapStateToProps)(DropDownControl);
diff --git a/app/client/src/components/formControls/EntitySelectorControl.tsx b/app/client/src/components/formControls/EntitySelectorControl.tsx
index 99f7cf8daa..2510bd0901 100644
--- a/app/client/src/components/formControls/EntitySelectorControl.tsx
+++ b/app/client/src/components/formControls/EntitySelectorControl.tsx
@@ -1,11 +1,9 @@
import React from "react";
import FormControl from "pages/Editor/FormControl";
import styled from "styled-components";
-import FormLabel from "components/editorComponents/FormLabel";
import { ControlProps } from "./BaseControl";
import { Colors } from "constants/Colors";
import Icon, { IconSize } from "components/ads/Icon";
-import { getBindingOrConfigPathsForEntitySelectorControl } from "entities/Action/actionProperties";
import { allowedControlTypes } from "components/formControls/utils";
const dropDownFieldConfig: any = {
@@ -39,15 +37,6 @@ const EntitySelectorContainer = styled.div`
justify-content: space-between;
`;
-export const StyledBottomLabel = styled(FormLabel)`
- margin-top: 5px;
- margin-left: 5px;
- font-weight: 400;
- font-size: 12px;
- color: ${Colors.GREY_7};
- line-height: 16px;
-`;
-
function EntitySelectorComponent(props: any) {
const { configProperty, schema } = props;
@@ -61,25 +50,20 @@ function EntitySelectorComponent(props: any) {
};
return (
-
+
{schema &&
schema.length > 0 &&
schema.map((singleSchema: any, index: number) => {
- const columnPath = getBindingOrConfigPathsForEntitySelectorControl(
- configProperty,
- index,
- );
return (
allowedControlTypes.includes(singleSchema.controlType) && (
- <>
+
{singleSchema.controlType === "DROP_DOWN" ? (
@@ -89,19 +73,19 @@ function EntitySelectorComponent(props: any) {
...inputFieldConfig,
...singleSchema,
customStyles,
- configProperty: columnPath,
- key: columnPath,
+ key: `ES_${singleSchema.configProperty}`,
}}
formName={props.formName}
/>
)}
{index < schema.length - 1 && (
)}
- >
+
)
);
})}
@@ -109,6 +93,8 @@ function EntitySelectorComponent(props: any) {
);
}
+// This is a wrapper component that just encapsulated the children dropdown and dynamic text
+// components & changes their appearance
export default function EntitySelectorControl(
props: EntitySelectorControlProps,
) {
@@ -122,7 +108,7 @@ export default function EntitySelectorControl(
diff --git a/app/client/src/pages/Editor/FormControl.tsx b/app/client/src/pages/Editor/FormControl.tsx
index a2f6673819..97f1809d81 100644
--- a/app/client/src/pages/Editor/FormControl.tsx
+++ b/app/client/src/pages/Editor/FormControl.tsx
@@ -51,7 +51,7 @@ function FormControl(props: FormControlProps) {
props.formName,
props?.multipleConfig,
),
- [],
+ [props],
);
if (hidden) return null;
@@ -133,7 +133,13 @@ function FormConfig(props: FormConfigProps) {
);
}
-export default memo(FormControl);
+// Updated the memo function to allow for disabled props to be compared
+export default memo(FormControl, (prevProps, nextProps) => {
+ return (
+ prevProps === nextProps &&
+ prevProps.config.disabled === nextProps.config.disabled
+ );
+});
function renderFormConfigTop(props: { config: ControlProps }) {
const {
diff --git a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx
index a9805fe6f0..3d87efe9d3 100644
--- a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx
+++ b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx
@@ -80,7 +80,6 @@ import Spinner from "components/ads/Spinner";
import {
ConditionalOutput,
FormEvalOutput,
- DynamicValues,
} from "reducers/evaluationReducers/formEvaluationReducer";
const QueryFormContainer = styled.form`
@@ -598,6 +597,7 @@ export function EditorJSONtoForm(props: Props) {
}
};
+ // Extract the output of conditionals attached to the form from the state
const extractConditionalOutput = (section: any): ConditionalOutput => {
let conditionalOutput: ConditionalOutput = {};
if (
@@ -649,67 +649,35 @@ export function EditorJSONtoForm(props: Props) {
};
// Function to modify the section config based on the output of evaluations
- const modifySectionConfig = (
- section: any,
- enabled: boolean,
- dynamicFetchedValues: DynamicValues | undefined,
- ): any => {
+ const modifySectionConfig = (section: any, enabled: boolean): any => {
if (!enabled) {
section.disabled = true;
} else {
section.disabled = false;
}
- if (!!dynamicFetchedValues) {
- section.dynamicFetchedValues = dynamicFetchedValues;
- }
return section;
};
- // Function to extract the object for dynamicValues if it is there in the evaluation state
- const extractDynamicValuesIfPresent = (
- conditionalOutput: ConditionalOutput,
- ) => {
- // By default, the section is enabled. This is to allow for the case where no conditional is provided.
- // The evaluation state disables the section if the condition is not met. (Checkout formEval.ts)
- let dynamicFetchedValues: DynamicValues | undefined;
- if (conditionalOutput.hasOwnProperty("fetchDynamicValues")) {
- dynamicFetchedValues = conditionalOutput.fetchDynamicValues;
- }
- return dynamicFetchedValues;
- };
-
// Render function to render the V2 of form editor type (UQI)
// Section argument is a nested config object, this function recursively renders the UI based on the config
const renderEachConfigV2 = (formName: string, section: any, idx: number) => {
let enabled = true;
- let dynamicFetchedValues: DynamicValues | undefined;
if (!!section) {
+ // If the section is a nested component, recursively check for conditional statements
if ("schema" in section && section.schema.length > 0) {
- section.schema.forEach((subSection: any, index: number) => {
- const configPropertyOfSubSection = `${
- section.configProperty
- }.column_${index + 1}`;
+ section.schema.forEach((subSection: any) => {
const conditionalOutput = extractConditionalOutput({
...subSection,
- configProperty: configPropertyOfSubSection,
});
enabled = checkIfSectionIsEnabled(conditionalOutput);
- dynamicFetchedValues = extractDynamicValuesIfPresent(
- conditionalOutput,
- );
- subSection = modifySectionConfig(
- subSection,
- enabled,
- dynamicFetchedValues,
- );
+ subSection = modifySectionConfig(subSection, enabled);
});
}
// If the component is not allowed to render, return null
const conditionalOutput = extractConditionalOutput(section);
if (!checkIfSectionCanRender(conditionalOutput)) return null;
enabled = checkIfSectionIsEnabled(conditionalOutput);
- dynamicFetchedValues = extractDynamicValuesIfPresent(conditionalOutput);
}
if (section.hasOwnProperty("controlType")) {
// If component is type section, render it's children
@@ -723,11 +691,7 @@ export function EditorJSONtoForm(props: Props) {
}
try {
const { configProperty } = section;
- const modifiedSection = modifySectionConfig(
- section,
- enabled,
- dynamicFetchedValues,
- );
+ const modifiedSection = modifySectionConfig(section, enabled);
return (
diff --git a/app/client/src/sagas/FormEvaluationSaga.ts b/app/client/src/sagas/FormEvaluationSaga.ts
index 5b13aeb0b5..201688098c 100644
--- a/app/client/src/sagas/FormEvaluationSaga.ts
+++ b/app/client/src/sagas/FormEvaluationSaga.ts
@@ -87,16 +87,18 @@ function* setFormEvaluationSagaAsync(
// Once all the actions are done, extract the actions that need to be fetched dynamically
const formId = action.payload.formId;
const evalOutput = workerResponse[formId];
- const queueOfValuesToBeFetched = extractQueueOfValuesToBeFetched(
- evalOutput,
- );
- // Pass the queue to the saga to fetch the dynamic values
- yield call(
- fetchDynamicValuesSaga,
- queueOfValuesToBeFetched,
- formId,
- evalOutput,
- );
+ if (!!evalOutput && typeof evalOutput === "object") {
+ const queueOfValuesToBeFetched = extractQueueOfValuesToBeFetched(
+ evalOutput,
+ );
+ // Pass the queue to the saga to fetch the dynamic values
+ yield call(
+ fetchDynamicValuesSaga,
+ queueOfValuesToBeFetched,
+ formId,
+ evalOutput,
+ );
+ }
}
} catch (e) {
log.error(e);
@@ -112,11 +114,10 @@ function* fetchDynamicValuesSaga(
evalOutput: FormEvalOutput,
) {
for (const key of Object.keys(queueOfValuesToBeFetched)) {
- evalOutput = yield call(
+ evalOutput[key].fetchDynamicValues = yield call(
fetchDynamicValueSaga,
queueOfValuesToBeFetched[key],
- key,
- evalOutput,
+ Object.assign({}, evalOutput[key].fetchDynamicValues as DynamicValues),
);
}
// Set the values to the state once all values are fetched
@@ -128,34 +129,31 @@ function* fetchDynamicValuesSaga(
function* fetchDynamicValueSaga(
value: ConditionalOutput,
- key: string,
- evalOutput: FormEvalOutput,
+ dynamicFetchedValues: DynamicValues,
) {
try {
const { config } = value.fetchDynamicValues as DynamicValues;
const { url } = config;
- (evalOutput[key].fetchDynamicValues as DynamicValues).hasStarted = true;
+ dynamicFetchedValues.hasStarted = true;
// Call the API to fetch the dynamic values
const response = yield call(PluginsApi.fetchDynamicFormValues, url);
- (evalOutput[key].fetchDynamicValues as DynamicValues).isLoading = false;
+ dynamicFetchedValues.isLoading = false;
if (!!response && response instanceof Array) {
- (evalOutput[key].fetchDynamicValues as DynamicValues).data = response;
- (evalOutput[key]
- .fetchDynamicValues as DynamicValues).hasFetchFailed = false;
+ dynamicFetchedValues.data = response;
+ dynamicFetchedValues.hasFetchFailed = false;
} else {
- (evalOutput[key]
- .fetchDynamicValues as DynamicValues).hasFetchFailed = true;
- (evalOutput[key].fetchDynamicValues as DynamicValues).data = [];
+ dynamicFetchedValues.hasFetchFailed = true;
+ dynamicFetchedValues.data = [];
}
} catch (e) {
log.error(e);
- (evalOutput[key].fetchDynamicValues as DynamicValues).hasFetchFailed = true;
- (evalOutput[key].fetchDynamicValues as DynamicValues).isLoading = false;
- (evalOutput[key].fetchDynamicValues as DynamicValues).data = [];
+ dynamicFetchedValues.hasFetchFailed = true;
+ dynamicFetchedValues.isLoading = false;
+ dynamicFetchedValues.data = [];
}
- return evalOutput;
+ return dynamicFetchedValues;
}
function* formEvaluationChangeListenerSaga() {
diff --git a/app/client/src/selectors/formSelectors.ts b/app/client/src/selectors/formSelectors.ts
index 5a7bcd5b79..ec0d8f571d 100644
--- a/app/client/src/selectors/formSelectors.ts
+++ b/app/client/src/selectors/formSelectors.ts
@@ -1,13 +1,17 @@
import { getFormValues, isValid, getFormInitialValues } from "redux-form";
import { AppState } from "reducers";
import { ActionData } from "reducers/entityReducers/actionsReducer";
-import { FormEvaluationState } from "reducers/evaluationReducers/formEvaluationReducer";
+import {
+ DynamicValues,
+ FormEvaluationState,
+} from "reducers/evaluationReducers/formEvaluationReducer";
import { createSelector } from "reselect";
-import _ from "lodash";
+import { replace } from "lodash";
import { getDataTree } from "./dataTreeSelectors";
import { DataTree } from "entities/DataTree/dataTreeFactory";
import { Action } from "entities/Action";
import { EvaluationError } from "utils/DynamicBindingUtils";
+import { getActionIdFromURL } from "pages/Editor/Explorer/helpers";
type GetFormData = (
state: AppState,
@@ -30,6 +34,16 @@ export const getApiName = (state: AppState, id: string) => {
export const getFormEvaluationState = (state: AppState): FormEvaluationState =>
state.evaluations.formEvaluation;
+// Selector to return the fetched values of the form components, only called for components that
+// have the fetchOptionsDynamically option set to true
+export const getDynamicFetchedValues = (
+ state: AppState,
+ configProperty: string,
+): DynamicValues =>
+ state.evaluations.formEvaluation[getActionIdFromURL() as string][
+ configProperty
+ ].fetchDynamicValues as DynamicValues;
+
type ConfigErrorProps = { configProperty: string; formName: string };
export const getConfigErrors = createSelector(
@@ -53,7 +67,7 @@ export const getConfigErrors = createSelector(
const actionError = action && action?.__evaluation__?.errors;
// get the configProperty for this form control and format it to resemble the format used in the action details errors object.
- const formattedConfig = _.replace(
+ const formattedConfig = replace(
configProperty,
"actionConfiguration",
"config",
diff --git a/app/client/src/workers/formEval.ts b/app/client/src/workers/formEval.ts
index 4b25394123..cdc1f45dfa 100644
--- a/app/client/src/workers/formEval.ts
+++ b/app/client/src/workers/formEval.ts
@@ -85,11 +85,8 @@ const generateInitialEvalState = (formConfig: FormConfig) => {
);
if ("schema" in formConfig && !!formConfig.schema)
- formConfig.schema.forEach((config: FormConfig, index: number) =>
- generateInitialEvalState({
- ...config,
- configProperty: `${formConfig.configProperty}.column_${index + 1}`,
- }),
+ formConfig.schema.forEach((config: FormConfig) =>
+ generateInitialEvalState({ ...config }),
);
};