Altinn / app-frontend-react

Altinn application React frontend
BSD 3-Clause "New" or "Revised" License
18 stars 31 forks source link

Analsysis: Should we allow (in a standardised way) checkbox support for individual data model fields for each alternative #789

Open allinox opened 3 years ago

allinox commented 3 years ago

Analysis description

See the description below. In grooming, there was a debate if we think this should be something we facilitate being possible.

Scope of analysis

Analysis

TBA

Conclusion

TBA

Original feature request

How it is now

As it is, the checkbox component stores all selected alternatives in one single field of the data model. However, some data models expect the choices to be stored in separate fields for each of the alternatives and this isn't possible with the current component.

I know that there is a work-around where you can make a new field in the data model for storing all choices made in the question before separating this into the different fields already existing in the data model through coding in CalculationHandler, but doing this for every such question adds a lot of extra hours to the work-load (especially considering that checkboxes are pretty commonly used in forms). For example, I'm currently working on a form with around 24 different checkbox questions, which means a lot of extra work and a lot of extra calculations that will have to run when using the app.

How I'd like it to be

I'd like the option to enter more than one data model field for storing, one for each alternative.

A suggestion

Developers are already given the choice of entering the alternatives manually or through a code list. Maybe a similar choice could be made between storage in one or multiple fields? If the default is to store in one, maybe a change like this would break less of the already existing apps? image

Additional context

There are likely a lot of forms in A2 that are currently using data models that expect the results of checkboxes to be stored in the fields of the individual alternatives, and thus their data ingestion system (mottakersystem) is expecting this as well. I imagine the extra cost to either fix their model and system for A3 compliance or configure the A3-apps for the current data models would be a pretty big issue for potential costumers.

ivarne commented 2 years ago

This should be looked at together with Altinn/app-frontend-react#114, because a natural way to implement different value strategies for checkboxes would be to use different datamodelbindings (as alternaltives to simpleBinding comma separated values)

ivarne commented 2 years ago

After my attempts at cleaning up the checkbox component in Altinn/altinn-studio#7718 and Altinn/altinn-studio#7775, I finally think I understand what the component does, well enough to do changes. My suggestion to support individual bindings would be the following change, and it seems to work, but unfortunately I'll need to spend a few more days trying to understand how the checkboxes component is displayed in other contexts (summary, pdf, groups...), so I can make the multiple binding approach work there as well as making it supported in Altinn studio.

diff --git a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/CheckboxesContainerComponent.tsx b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/CheckboxesContainerComponent.tsx
index 00005a735..4ad2d7305 100644
--- a/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/CheckboxesContainerComponent.tsx
+++ b/src/Altinn.Apps/AppFrontend/react/altinn-app-frontend/src/components/base/CheckboxesContainerComponent.tsx
@@ -15,6 +15,8 @@ export interface ICheckboxContainerProps extends IComponentProps {
   options: IOption[];
   optionsId: string;
   preselectedOptionIndex?: number;
+  checkedValue?: string;
+  uncheckedValue?: string;
 }

 export interface IStyledCheckboxProps extends CheckboxProps {
@@ -76,11 +78,14 @@ export const CheckboxContainerComponent = (props: ICheckboxContainerProps) => {
   const apiOptions: IOption[] = useAppSelector(state => state.optionState.options[props.optionsId]);
   const options = apiOptions || props.options || emptyList;
   const checkBoxesIsRow: boolean = options.length <= 2;
+  const checkedValue = props.checkedValue ?? "true";
+  const uncheckedValue = props.uncheckedValue ?? "false"

   const selected = React.useMemo(() => {
-    return props.formData.simpleBinding.split(',').map(v => options.find(o => o.value == v)).filter(o => !!o) ?? emptyList;
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [props.formData, options])
+    if (props.formData?.simpleBinding)
+      return props.formData.simpleBinding.split(',').map(v => options.find(o => o.value == v)).filter(o => !!o) ?? emptyList;
+    return Object.keys(props.formData).map(v => options.find(o => o.value == v)).filter(o => props.formData[o?.value] == checkedValue) ?? emptyList;
+  }, [props.formData, options, checkedValue])

   // Implement preselected functionality
   React.useEffect(() => {
@@ -103,8 +108,14 @@ export const CheckboxContainerComponent = (props: ICheckboxContainerProps) => {
     const newSelected = previouslySelected ?
       selected.filter(o => o.value !== event.target.name) :
       [...selected, options.find(o => o.value === event.target.name)];
-    props.handleDataChange(newSelected.map(o => o.value).join());
+    if(props.dataModelBindings["simpleBinding"]){
+      props.handleDataChange(newSelected.map(o => o.value).join(), "simpleBinding");
+    }
+
+    // Update box binding (if bindigns are set for each option)
+    if(props.dataModelBindings[event.target.name]){
+      props.handleDataChange(previouslySelected ? uncheckedValue : checkedValue, event.target.name)
+    }
   };

   const isOptionSelected = (option: IOption) => {
olemartinorg commented 1 year ago

I suspect this is a (fancier) duplicate of #273. Also, #681 is very relevant here. Moving to app-frontend-react.