diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index 16d16de8a2..359f287abe 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -227,9 +227,18 @@ export const getFrontendStore = () => {
})
},
save: async screen => {
+ const state = get(store)
const creatingNewScreen = screen._id === undefined
const savedScreen = await API.saveScreen(screen)
const routesResponse = await API.fetchAppRoutes()
+ let usedPlugins = state.usedPlugins
+
+ // If plugins changed we need to fetch the latest app metadata
+ if (savedScreen.pluginAdded) {
+ const { application } = await API.fetchAppPackage(state.appId)
+ usedPlugins = application.usedPlugins || []
+ }
+
store.update(state => {
// Update screen object
const idx = state.screens.findIndex(x => x._id === savedScreen._id)
@@ -248,6 +257,9 @@ export const getFrontendStore = () => {
// Update routes
state.routes = routesResponse.routes
+ // Update used plugins
+ state.usedPlugins = usedPlugins
+
return state
})
return savedScreen
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte
index cd86d2e87c..ff880cd5e8 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte
@@ -41,23 +41,10 @@
}
// Construct iframe template
- $: pluginLinks = generatePluginLinks($store.usedPlugins)
- $: template = iframeTemplate
- .replace(/\{\{ CLIENT_LIB_PATH }}/, $store.clientLibPath)
- .replace(/\{\{ PLUGINS }}/, pluginLinks)
-
- const generatePluginLinks = plugins => {
- if (!plugins?.length) {
- return ""
- }
- return plugins
- .map(plugin => {
- // Split up like this as otherwise parsing fails because the script
- // tags confuse svelte
- return `<` + `script src="/plugins/${plugin.jsUrl}">` + `script>`
- })
- .join("")
- }
+ $: template = iframeTemplate.replace(
+ /\{\{ CLIENT_LIB_PATH }}/,
+ $store.clientLibPath
+ )
const placeholderScreen = new Screen()
.name("Screen Placeholder")
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js
index 2b24ee4fa0..8711f5d670 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/iframeTemplate.js
@@ -37,7 +37,6 @@ export default `
}
- {{ PLUGINS }}
{#if $auth.isAdmin}
@@ -106,6 +112,7 @@
}
}}
/>
+
diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte
index 884f6e8ea1..0b57b91ff6 100644
--- a/packages/client/src/components/ClientApp.svelte
+++ b/packages/client/src/components/ClientApp.svelte
@@ -89,6 +89,14 @@
})
+
+ {#if $builderStore.usedPlugins?.length}
+ {#each $builderStore.usedPlugins as plugin}
+
+ {/each}
+ {/if}
+
+
{#if dataLoaded}
{
+ const initialise = (instance, force = false) => {
if (instance == null) {
return
}
// Ensure we're processing a new instance
const instanceKey = Helpers.hashString(JSON.stringify(instance))
- if (instanceKey === lastInstanceKey) {
+ if (instanceKey === lastInstanceKey && !force) {
return
} else {
lastInstanceKey = instanceKey
@@ -407,9 +407,11 @@
!componentStore.actions.isComponentRegistered(id)
) {
componentStore.actions.registerInstance(id, {
+ component: instance._component,
getSettings: () => cachedSettings,
getRawSettings: () => ({ ...staticSettings, ...dynamicSettings }),
getDataContext: () => get(context),
+ reload: () => initialise(instance, true),
})
}
})
diff --git a/packages/client/src/components/devtools/DevToolsStatsTab.svelte b/packages/client/src/components/devtools/DevToolsStatsTab.svelte
index b20b9fafa0..24f587332c 100644
--- a/packages/client/src/components/devtools/DevToolsStatsTab.svelte
+++ b/packages/client/src/components/devtools/DevToolsStatsTab.svelte
@@ -19,7 +19,10 @@
label="Active screen"
value={$screenStore.activeScreen?.routing.route}
/>
-
+
diff --git a/packages/client/src/index.js b/packages/client/src/index.js
index 32b242bc69..bef3ab9c12 100644
--- a/packages/client/src/index.js
+++ b/packages/client/src/index.js
@@ -27,6 +27,7 @@ const loadBudibase = () => {
previewDevice: window["##BUDIBASE_PREVIEW_DEVICE##"],
navigation: window["##BUDIBASE_PREVIEW_NAVIGATION##"],
hiddenComponentIds: window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"],
+ usedPlugins: window["##BUDIBASE_USED_PLUGINS##"],
})
// Set app ID - this window flag is set by both the preview and the real
@@ -39,6 +40,11 @@ const loadBudibase = () => {
devToolsStore.actions.setEnabled(enableDevTools)
// Register any custom components
+ window.registerCustomComponent = plugin => {
+ componentStore.actions.registerCustomComponent(plugin)
+ console.log("registered!")
+ loadBudibase()
+ }
if (window["##BUDIBASE_CUSTOM_COMPONENTS##"]) {
window["##BUDIBASE_CUSTOM_COMPONENTS##"].forEach(component => {
componentStore.actions.registerCustomComponent(component)
diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js
index a07c9da996..94dd16a957 100644
--- a/packages/client/src/stores/components.js
+++ b/packages/client/src/stores/components.js
@@ -10,7 +10,11 @@ import * as AppComponents from "../components/app/index.js"
const budibasePrefix = "@budibase/standard-components/"
const createComponentStore = () => {
- const store = writable({})
+ const store = writable({
+ customComponentManifest: {},
+ componentsAwaitingConstructors: {},
+ mountedComponents: {},
+ })
const derivedStore = derived(
[store, builderStore, devToolsStore, screenStore],
@@ -29,9 +33,7 @@ const createComponentStore = () => {
asset = $screenState.activeScreen
}
const component = findComponentById(asset?.props, selectedComponentId)
- const prefix = "@budibase/standard-components/"
- const type = component?._component?.replace(prefix, "")
- const definition = type ? Manifest[type] : null
+ const definition = getComponentDefinition(component?._component)
// Derive the selected component path
const path =
@@ -39,32 +41,50 @@ const createComponentStore = () => {
return {
customComponentManifest: $store.customComponentManifest,
- selectedComponentInstance: $store[selectedComponentId],
+ selectedComponentInstance:
+ $store.mountedComponents[selectedComponentId],
selectedComponent: component,
selectedComponentDefinition: definition,
selectedComponentPath: path?.map(component => component._id),
- mountedComponents: Object.keys($store).length,
+ mountedComponentCount: Object.keys($store.mountedComponents).length,
currentAsset: asset,
}
}
)
const registerInstance = (id, instance) => {
- store.update(state => ({
- ...state,
- [id]: instance,
- }))
+ store.update(state => {
+ // If this is a custom component and does not have an implementation yet,
+ // store so we can reload this component later
+ const component = instance.component
+ let cac = state.componentsAwaitingConstructors
+ if (!getComponentConstructor(component)) {
+ if (!cac[component]) {
+ cac[component] = []
+ }
+ cac[component].push(id)
+ }
+
+ return {
+ ...state,
+ componentsAwaitingConstructors: cac,
+ mountedComponents: {
+ ...state.mountedComponents,
+ [id]: instance,
+ },
+ }
+ })
}
const unregisterInstance = id => {
store.update(state => {
- delete state[id]
+ delete state.mountedComponents[id]
return state
})
}
const isComponentRegistered = id => {
- return get(store)[id] != null
+ return get(store).mountedComponents[id] != null
}
const getComponentById = id => {
@@ -117,17 +137,32 @@ const createComponentStore = () => {
if (!Component || !schema?.schema?.name) {
return
}
+ const componentName = `plugin/${schema.schema.name}/1.0.0`
store.update(state => {
if (!state.customComponentManifest) {
state.customComponentManifest = {}
}
- const componentName = `plugin/${schema.schema.name}/1.0.0`
state.customComponentManifest[componentName] = {
schema,
Component,
}
return state
})
+
+ // Reload any mounted components which depend on this definition
+ const state = get(store)
+ if (state.componentsAwaitingConstructors[componentName]?.length) {
+ state.componentsAwaitingConstructors[componentName].forEach(id => {
+ const instance = state.mountedComponents[id]
+ if (instance) {
+ instance.reload()
+ }
+ })
+ store.update(state => {
+ delete state.componentsAwaitingConstructors[componentName]
+ return state
+ })
+ }
}
return {
diff --git a/packages/server/src/api/controllers/screen.js b/packages/server/src/api/controllers/screen.js
index 52a22f1073..1d5d876dc4 100644
--- a/packages/server/src/api/controllers/screen.js
+++ b/packages/server/src/api/controllers/screen.js
@@ -41,6 +41,7 @@ exports.save = async ctx => {
// Find any custom components being used
let pluginNames = []
+ let pluginAdded = false
findPlugins(screen.props, pluginNames)
if (pluginNames.length) {
const globalDB = getGlobalDB()
@@ -62,7 +63,6 @@ exports.save = async ctx => {
const application = await db.get(DocumentTypes.APP_METADATA)
let usedPlugins = application.usedPlugins || []
- let pluginAdded = false
requiredPlugins.forEach(plugin => {
if (!usedPlugins.find(x => x._id === plugin._id)) {
pluginAdded = true
@@ -76,7 +76,6 @@ exports.save = async ctx => {
})
if (pluginAdded) {
- console.log("plugin added! new plugins", usedPlugins)
await updateAppPackage({ usedPlugins }, ctx.appId)
}
}
@@ -89,6 +88,7 @@ exports.save = async ctx => {
...screen,
_id: response.id,
_rev: response.rev,
+ pluginAdded,
}
}