fix: cache controllers and use Promise.all for concurrent execution

This commit is contained in:
Shariq Ansari 2025-05-09 15:22:40 +05:30
parent 07b2d9f792
commit 556386e446
2 changed files with 51 additions and 56 deletions

View File

@ -3,6 +3,7 @@ import { createToast } from '@/utils'
import { createDocumentResource } from 'frappe-ui' import { createDocumentResource } from 'frappe-ui'
const documentsCache = {} const documentsCache = {}
const controllersCache = {}
export function useDocument(doctype, docname) { export function useDocument(doctype, docname) {
const { setupScript } = getScript(doctype) const { setupScript } = getScript(doctype)
@ -35,63 +36,59 @@ export function useDocument(doctype, docname) {
} }
function setupFormScript() { function setupFormScript() {
if (documentsCache[doctype][docname]['controllers']) return if (controllersCache[doctype]) return
const controllers = setupScript(documentsCache[doctype][docname]) controllersCache[doctype] = setupScript(documentsCache[doctype][docname])
if (!controllers) return
documentsCache[doctype][docname]['controllers'] = controllers
} }
function getControllers(row = null, dt = null) { function getControllers(row = null) {
let controllers = documentsCache[doctype][docname]?.controllers || {} const _doctype = row?.doctype || doctype
if (Object.keys(controllers).length === 0) return return (controllersCache[doctype] || []).filter(
(c) => c.constructor.name === _doctype.replace(/\s+/g, ''),
dt = dt || doctype
if (!controllers[dt].length) return []
const _dt = row?.doctype ? row.doctype : doctype
const _controllers = controllers[dt].filter(
(c) => c.constructor.name === _dt.replace(/\s+/g, ''),
) )
return _controllers || []
} }
async function triggerOnRefresh() { async function triggerOnRefresh() {
const controllers = getControllers() const controllers = getControllers()
if (!controllers.length) return if (!controllers.length) return
for (const c of controllers) {
await c.refresh() await Promise.all(
} controllers.map(async (c) => {
await c.refresh()
}),
)
} }
async function triggerOnChange(fieldname, row) { async function triggerOnChange(fieldname, row) {
const controllers = getControllers(row) const controllers = getControllers(row)
if (!controllers.length) return if (!controllers.length) return
for (const c of controllers) { await Promise.all(
if (row) { controllers.map(async (c) => {
c.currentRowIdx = row.idx if (row) {
c.value = row[fieldname] c.currentRowIdx = row.idx
c.oldValue = getOldValue(fieldname, row) c.value = row[fieldname]
} else { c.oldValue = getOldValue(fieldname, row)
c.value = documentsCache[doctype][docname].doc[fieldname] } else {
c.oldValue = getOldValue(fieldname) c.value = documentsCache[doctype][docname].doc[fieldname]
} c.oldValue = getOldValue(fieldname)
await c[fieldname]?.() }
} await c[fieldname]?.()
}),
)
} }
async function triggerOnRowAdd(row) { async function triggerOnRowAdd(row) {
const controllers = getControllers(row) const controllers = getControllers(row)
if (!controllers.length) return if (!controllers.length) return
for (const c of controllers) { await Promise.all(
c.currentRowIdx = row.idx controllers.map(async (c) => {
c.value = row c.currentRowIdx = row.idx
c.value = row
await c[row.parentfield + '_add']?.() await c[row.parentfield + '_add']?.()
} }),
)
} }
async function triggerOnRowRemove(selectedRows, rows) { async function triggerOnRowRemove(selectedRows, rows) {
@ -99,19 +96,21 @@ export function useDocument(doctype, docname) {
const controllers = getControllers(rows[0]) const controllers = getControllers(rows[0])
if (!controllers.length) return if (!controllers.length) return
for (const c of controllers) { await Promise.all(
if (selectedRows.size === 1) { controllers.map(async (c) => {
const selectedRow = Array.from(selectedRows)[0] if (selectedRows.size === 1) {
c.currentRowIdx = rows.find((r) => r.name === selectedRow).idx const selectedRow = Array.from(selectedRows)[0]
} else { c.currentRowIdx = rows.find((r) => r.name === selectedRow).idx
delete c.currentRowIdx } else {
} delete c.currentRowIdx
}
c.selectedRows = Array.from(selectedRows) c.selectedRows = Array.from(selectedRows)
c.rows = rows c.rows = rows
await c[rows[0].parentfield + '_remove']?.() await c[rows[0].parentfield + '_remove']?.()
} }),
)
} }
function getOldValue(fieldname, row) { function getOldValue(fieldname, row) {

View File

@ -47,7 +47,7 @@ export function getScript(doctype, view = 'Form') {
} }
function setupMultipleFormControllers(scriptStrings, document, helpers) { function setupMultipleFormControllers(scriptStrings, document, helpers) {
const controllers = {} const controllers = []
let parentInstanceIdx = null let parentInstanceIdx = null
for (let scriptName in scriptStrings) { for (let scriptName in scriptStrings) {
@ -66,23 +66,19 @@ export function getScript(doctype, view = 'Form') {
let { doctypeMeta } = getMeta(doctype) let { doctypeMeta } = getMeta(doctype)
if (!controllers[doctype]) {
controllers[doctype] = []
}
// if className is not doctype name, then it is a child doctype // if className is not doctype name, then it is a child doctype
let isChildDoctype = className !== doctypeName let isChildDoctype = className !== doctypeName
if (isChildDoctype) { if (isChildDoctype) {
if (!controllers[doctype].length) { if (!controllers.length) {
console.error( console.error(
`⚠️ No class found for doctype: ${doctype}, it is mandatory to have a class for the parent doctype. it can be empty, but it should be present.`, `⚠️ No class found for doctype: ${doctype}, it is mandatory to have a class for the parent doctype. it can be empty, but it should be present.`,
) )
return return
} }
parentInstance = controllers[doctype][parentInstanceIdx] parentInstance = controllers[parentInstanceIdx]
} else { } else {
parentInstanceIdx = controllers[doctype].length || 0 parentInstanceIdx = controllers.length || 0
} }
const instance = setupFormController( const instance = setupFormController(
@ -93,7 +89,7 @@ export function getScript(doctype, view = 'Form') {
isChildDoctype, isChildDoctype,
) )
controllers[doctype].push(instance) controllers.push(instance)
}) })
} catch (err) { } catch (err) {
console.error('Failed to load form controller:', err) console.error('Failed to load form controller:', err)