diff --git a/frontend/src/components/ResourceDialog.vue b/frontend/src/components/ResourceDialog.vue
index ddbe4942d..6b1d92faa 100644
--- a/frontend/src/components/ResourceDialog.vue
+++ b/frontend/src/components/ResourceDialog.vue
@@ -144,6 +144,13 @@
+
+
({ ...emptyResource })
diff --git a/frontend/src/data/studioResources.ts b/frontend/src/data/studioResources.ts
index 775e4e808..bd3cc0381 100644
--- a/frontend/src/data/studioResources.ts
+++ b/frontend/src/data/studioResources.ts
@@ -7,6 +7,7 @@ export const studioPageResources = createListResource({
"resource_type",
"resource_name",
"auto",
+ "cache",
"fields",
"filters",
"limit",
diff --git a/frontend/src/pages/AppContainer.vue b/frontend/src/pages/AppContainer.vue
index 850baedca..cdacc9836 100644
--- a/frontend/src/pages/AppContainer.vue
+++ b/frontend/src/pages/AppContainer.vue
@@ -24,39 +24,47 @@ const codeStore = useCodeStore()
const page = ref(null)
const rootBlock = ref(null)
+let renderedPath: string | null = null
-async function loadPage() {
- let { pageRoute } = route.params as { pageRoute: string[] }
- const isDynamic = route.meta?.isDynamic
-
- let currentPath = "/"
- if (isDynamic) {
- currentPath = route.matched?.[0]?.path
- } else if (pageRoute) {
- currentPath = pageRoute[0]
- }
-
+async function loadPage(force: boolean = false) {
+ const currentPath = resolvePagePath()
if (!currentPath) {
rootBlock.value = null
+ renderedPath = null
return
}
- page.value = await findPageWithRoute(window.app_name, currentPath)
+ const isSamePage = !force && currentPath === renderedPath
+ if (!isSamePage) {
+ const nextPage = await findPageWithRoute(window.app_name, currentPath)
+ if (!nextPage) return
+ page.value = nextPage
+ }
if (!page.value) return
+
await store.setPageData(page.value)
await codeStore.setPageScript(page.value, Boolean(page.value.is_standard))
+ if (isSamePage) return
+
const blocks = window.is_preview
? JSON.parse(page.value?.draft_blocks || page.value?.blocks)
: JSON.parse(page.value?.blocks)
if (blocks) {
rootBlock.value = getBlockInstance(blocks[0])
+ renderedPath = currentPath
}
}
-watch(() => route.path, loadPage, { immediate: true })
+watch(() => route.path, () => loadPage(), { immediate: true })
-if (window.is_preview) useLivePreview(page, loadPage)
+if (window.is_preview) useLivePreview(page, () => loadPage(true))
+
+function resolvePagePath() {
+ if (route.meta?.isDynamic) return route.matched?.[0]?.path
+ const { pageRoute } = route.params as { pageRoute: string[] }
+ return pageRoute ? pageRoute[0] : "/"
+}
usePageMeta(() => {
return {
diff --git a/frontend/src/stores/codeStore.ts b/frontend/src/stores/codeStore.ts
index ba7e836ce..7a49248a2 100644
--- a/frontend/src/stores/codeStore.ts
+++ b/frontend/src/stores/codeStore.ts
@@ -13,7 +13,7 @@ import * as globalUtils from "@/utils/globalUtils"
import { getInitialVariableValue, getValueFromObject, setValueInObject } from "@/utils/helpers"
import { isDynamicValue, normalizeDynamicValue } from "@/utils/code"
import { isFunctionExpression, toOptionalChaining, getTopLevelBindings } from "@/utils/parseCode"
-import type { Filters, Resource, DocumentResource, DataResult } from "@/types/Studio/StudioResource"
+import type { Filters, Resource, APIResource, DocumentResource, DataResult } from "@/types/Studio/StudioResource"
import type { StudioPage } from "@/types/Studio/StudioPage"
import type { Variable } from "@/types/Studio/StudioPageVariable"
import type { ExpressionEvaluationContext } from "@/types"
@@ -55,7 +55,6 @@ const useCodeStore = defineStore("codeStore", () => {
async function setPageResources(page: StudioPage, setResourceConfig: boolean = false) {
studioPageResources.filters = { parent: page.name }
await studioPageResources.reload()
- resources.value = {}
const resourcePromises = studioPageResources.data.map(async (resource: Resource) => {
const newResource = await getNewResource(resource, {
@@ -73,14 +72,16 @@ const useCodeStore = defineStore("codeStore", () => {
const resolvedResources = await Promise.all(resourcePromises)
+ const nextResources: Record = {}
resolvedResources.forEach((item) => {
- resources.value[item.resource_name] = item.value
+ nextResources[item.resource_name] = item.value
if (setResourceConfig) {
if (!item.value) return
- resources.value[item.resource_name].resource_id = item.resource_id
- resources.value[item.resource_name].resource_type = item.resource_type
+ nextResources[item.resource_name].resource_id = item.resource_id
+ nextResources[item.resource_name].resource_type = item.resource_type
}
})
+ resources.value = nextResources
}
async function setPageVariables(page: StudioPage) {
@@ -449,7 +450,7 @@ const useCodeStore = defineStore("codeStore", () => {
switch (resource.resource_type) {
case "Document":
return getDocumentResource(resource, context)
- case "Document List":
+ case "Document List": {
const params: any = {
doctype: resource.document_type,
fields: fields.length ? fields : "*",
@@ -463,18 +464,27 @@ const useCodeStore = defineStore("codeStore", () => {
params["orderBy"] = `${resource.sort_field} ${resource.sort_order}`
}
return createListResource(params)
- case "API Resource":
+ }
+ case "API Resource": {
+ const apiParams = getAPIParams(resource.params, context)
return createResource({
url: resource.url,
method: resource.method,
- params: getAPIParams(resource.params, context),
+ params: apiParams,
auto: resource.auto,
+ cache: getCacheOption(resource, apiParams),
...getTransforms(resource),
...getSuccessErrorHandlers(resource),
})
+ }
}
}
+ function getCacheOption(resource: APIResource, params: Record | string | null) {
+ if (!resource.cache) return undefined
+ return [resource.resource_name, resource.url, resource.method, JSON.stringify(params)]
+ }
+
function getAPIParams(params: Record | string | null = null, context: ExpressionEvaluationContext) {
if (!params) return null
if (typeof params === "string") {
diff --git a/frontend/src/types/Studio/StudioResource.ts b/frontend/src/types/Studio/StudioResource.ts
index 0c6e7c931..a71f9ee8a 100644
--- a/frontend/src/types/Studio/StudioResource.ts
+++ b/frontend/src/types/Studio/StudioResource.ts
@@ -9,6 +9,8 @@ interface BaseResource {
resource_type: ResourceType
/** Whether to automatically fetch data on first load */
auto?: boolean
+ /** Whether to reuse the resource and its data across page loads, keyed on url + params */
+ cache?: boolean
transform?: string | null
on_success?: string
on_error?: string
diff --git a/studio/studio/doctype/studio_page_resource/studio_page_resource.json b/studio/studio/doctype/studio_page_resource/studio_page_resource.json
index 3494c9c17..47016e351 100644
--- a/studio/studio/doctype/studio_page_resource/studio_page_resource.json
+++ b/studio/studio/doctype/studio_page_resource/studio_page_resource.json
@@ -14,6 +14,7 @@
"sort_field",
"sort_order",
"auto",
+ "cache",
"document_resource_section",
"document_type",
"column_break_mkdu",
@@ -173,6 +174,13 @@
"fieldtype": "Check",
"in_list_view": 1,
"label": "Auto fetch data on load"
+ },
+ {
+ "default": "0",
+ "description": "Reuse this resource and its data across page loads, keyed on url + params",
+ "fieldname": "cache",
+ "fieldtype": "Check",
+ "label": "Cache data across page loads"
}
],
"grid_page_length": 50,