From 02d6a7d63cad0b5b8260df658ab3b79e4082920a Mon Sep 17 00:00:00 2001 From: Ole Martin Handeland Date: Mon, 27 Apr 2026 12:42:12 +0200 Subject: [PATCH] Testing out spinner when fetching List data, along with toggleable prefetching feature --- src/global.ts | 2 ++ src/layout/List/ListComponent.tsx | 7 ++++- src/utils/layout/generator/NodeGenerator.tsx | 27 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/global.ts b/src/global.ts index 847fa9f59c..6dabaad90e 100644 --- a/src/global.ts +++ b/src/global.ts @@ -42,6 +42,8 @@ declare global { conditionalRuleHandlerObject: IRuleObject; ruleHandlerObject: IRuleObject; + listPrefetchingEnabled: boolean | undefined; + /** * In React components, hierarchy generators, or other places that are run continuously, use window.logErrorOnce() instead * @see window.logErrorOnce diff --git a/src/layout/List/ListComponent.tsx b/src/layout/List/ListComponent.tsx index bc29aa33d5..7955bb544f 100644 --- a/src/layout/List/ListComponent.tsx +++ b/src/layout/List/ListComponent.tsx @@ -13,6 +13,7 @@ import { import cn from 'classnames'; import { Pagination as CustomPagination } from 'src/app-components/Pagination/Pagination'; +import { AltinnSpinner } from 'src/components/AltinnSpinner'; import { Description } from 'src/components/form/Description'; import { RadioButton } from 'src/components/form/RadioButton'; import { RequiredIndicator } from 'src/components/form/RequiredIndicator'; @@ -74,7 +75,7 @@ export const ListComponent = ({ baseComponentId }: PropsFromGenericComponent<'Li sortDirection, }; - const { data } = useDataListQuery(filter, dataListId, secure, mapping, queryParameters); + const { data, isFetching } = useDataListQuery(filter, dataListId, secure, mapping, queryParameters); const bindings = item.dataModelBindings ?? ({} as IDataModelBindingsForList); // Determine selection mode based on bindings @@ -171,6 +172,10 @@ export const ListComponent = ({ baseComponentId }: PropsFromGenericComponent<'Li required, }); + if (isFetching) { + return ; + } + if (isMobile && !readOnly) { return ( diff --git a/src/utils/layout/generator/NodeGenerator.tsx b/src/utils/layout/generator/NodeGenerator.tsx index 62e3219636..115dc3dbfc 100644 --- a/src/utils/layout/generator/NodeGenerator.tsx +++ b/src/utils/layout/generator/NodeGenerator.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect, useMemo } from 'react'; import type { PropsWithChildren } from 'react'; +import { useDataListQuery } from 'src/features/dataLists/useDataListQuery'; import { evalExpr } from 'src/features/expressions'; import { ExprVal } from 'src/features/expressions/types'; import { ExprValidation } from 'src/features/expressions/validation'; @@ -11,6 +12,7 @@ import { useGeneratorErrorBoundaryNodeRef } from 'src/utils/layout/generator/Gen import { WhenParentAdded } from 'src/utils/layout/generator/GeneratorStages'; import { NodePropertiesValidation } from 'src/utils/layout/generator/validation/NodePropertiesValidation'; import { NodesInternal, NodesStore } from 'src/utils/layout/NodesContext'; +import { useExpressionDataSources } from 'src/utils/layout/useExpressionDataSources'; import type { SimpleEval } from 'src/features/expressions'; import type { ExprResolved, ExprValToActual, ExprValToActualOrExpr } from 'src/features/expressions/types'; import type { FormComponentProps, SummarizableComponentProps } from 'src/layout/common.generated'; @@ -19,6 +21,7 @@ import type { CompExternalExact, CompIntermediate, CompIntermediateExact, + CompInternal, CompTypes, ITextResourceBindings, } from 'src/layout/layout'; @@ -55,6 +58,9 @@ export function NodeGenerator({ children, externalItem }: PropsWithChildren + {intermediateItem.type === 'List' && window.listPrefetchingEnabled !== false && ( + + )} ({ return null; } +function PrefetchListData({ item }: { item: CompIntermediateExact<'List'> }) { + const dataSources = useExpressionDataSources(item); + const props = useExpressionResolverProps<'List'>(`Invalid expression for ${item.id}`, item, dataSources); + const resolvedItem = getComponentDef('List').evalExpressions(props) as CompInternal<'List'>; + + useDataListQuery( + { + pageSize: resolvedItem.pagination?.default ?? 0, + pageNumber: 0, + sortColumn: undefined, + sortDirection: 'none', + }, + resolvedItem.dataListId, + resolvedItem.secure, + resolvedItem.mapping, + resolvedItem.queryParameters, + ); + + return null; +} + /** * Creates props for the expression resolver that can be used to evaluate expressions in a component configuration. * These props are passed on to your component's `evalExpressions` method.