From 9208a43cf1a689b782c107919116943c9f383630 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 7 Jul 2020 20:16:03 +0100 Subject: [PATCH] Form component now supports edit recprd --- packages/client/src/render/screenRouter.js | 4 +- packages/client/src/state/bbComponentApi.js | 3 + packages/client/src/state/stateManager.js | 8 ++- .../standard-components/src/DataForm.svelte | 56 +++++++++++++--- .../src/DataFormWide.svelte | 64 ++++++++++++++++--- 5 files changed, 113 insertions(+), 22 deletions(-) diff --git a/packages/client/src/render/screenRouter.js b/packages/client/src/render/screenRouter.js index 4f4256f9fe..2bd8595064 100644 --- a/packages/client/src/render/screenRouter.js +++ b/packages/client/src/render/screenRouter.js @@ -1,5 +1,5 @@ import regexparam from "regexparam" -import { routerStore } from "../state/store" +import { appStore } from "../state/store" import { getAppId } from "./getAppId" export const screenRouter = ({ screens, onScreenSelected, window }) => { @@ -49,7 +49,7 @@ export const screenRouter = ({ screens, onScreenSelected, window }) => { }) } - routerStore.update(state => { + appStore.update(state => { state["##routeParams"] = params return state }) diff --git a/packages/client/src/state/bbComponentApi.js b/packages/client/src/state/bbComponentApi.js index 112e3e8534..d426afac6e 100644 --- a/packages/client/src/state/bbComponentApi.js +++ b/packages/client/src/state/bbComponentApi.js @@ -8,6 +8,7 @@ export const bbFactory = ({ store, componentLibraries, onScreenSlotRendered, + getCurrentState, }) => { const apiCall = method => (url, body) => { return fetch(url, { @@ -53,6 +54,8 @@ export const bbFactory = ({ store: store, api, parent, + // these parameters are populated by screenRouter + routeParams: () => getCurrentState()["##routeParams"], } } } diff --git a/packages/client/src/state/stateManager.js b/packages/client/src/state/stateManager.js index ac417f32fa..67437a73f7 100644 --- a/packages/client/src/state/stateManager.js +++ b/packages/client/src/state/stateManager.js @@ -26,8 +26,14 @@ export const createStateManager = ({ routeTo, }) => { let handlerTypes = eventHandlers(routeTo) - let currentState + // creating a reference to the current state + // this avoids doing store.get() ... which is expensive on + // hot paths, according to the svelte docs. + // the state object reference never changes (although it's internals do) + // so this should work fine for us + let currentState + appStore.subscribe(s => (currentState = s)) const getCurrentState = () => currentState const bb = bbFactory({ diff --git a/packages/standard-components/src/DataForm.svelte b/packages/standard-components/src/DataForm.svelte index 767a781df4..74f3a0db1d 100644 --- a/packages/standard-components/src/DataForm.svelte +++ b/packages/standard-components/src/DataForm.svelte @@ -13,14 +13,14 @@ number: "number", } - let newModel = { - modelId: model, - } + let record let store = _bb.store let schema = {} let modelDef = {} let saved = false let saving = false + let recordId + let isNew = true let inputElements = {} @@ -30,6 +30,8 @@ $: fields = Object.keys(schema) + $: Object.values(inputElements).length && setForm(record) + async function fetchModel() { const FETCH_MODEL_URL = `/api/models/${model}` const response = await _bb.api.get(FETCH_MODEL_URL) @@ -42,7 +44,8 @@ if (saving) return saving = true const SAVE_RECORD_URL = `/api/${model}/records` - const response = await _bb.api.post(SAVE_RECORD_URL, newModel) + const response = await _bb.api.post(SAVE_RECORD_URL, record) + const json = await response.json() if (response.status === 200) { @@ -51,7 +54,13 @@ return state }) - resetForm() + // wipe form, if new record, otherwise update + // model to get new _rev + if (isNew) { + resetForm() + } else { + record = json + } // set saved, and unset after 1 second // i.e. make the success notifier appear, then disappear again after time @@ -72,29 +81,58 @@ el.checked = false } } - newModel = { + record = { modelId: model } } + const setForm = rec => { + if (isNew || !rec) return + for (let fieldName in inputElements) { + if (typeof rec[fieldName] === "boolean") { + inputElements[fieldName].checked = rec[fieldName] + } else { + inputElements[fieldName].value = rec[fieldName] + } + } + } + const handleInput = field => event => { let value if (event.target.type === "checkbox") { value = event.target.checked - newModel[field] = value + record[field] = value return } if (event.target.type === "number") { value = parseInt(event.target.value) - newModel[field] = value + record[field] = value return } value = event.target.value - newModel[field] = value + record[field] = value } + + onMount(() => { + const routeParams = _bb.routeParams() + recordId = Object.keys(routeParams).length > 0 && (routeParams.id || routeParams[0]) + isNew = !recordId || recordId === "new" + + if (isNew) { + record = { modelId: model } + } else { + const GET_RECORD_URL = `/api/${model}/records/${recordId}` + _bb.api.get(GET_RECORD_URL) + .then(response => response.json()) + .then(rec => { + record = rec + setForm(rec) + }) + } + });
diff --git a/packages/standard-components/src/DataFormWide.svelte b/packages/standard-components/src/DataFormWide.svelte index 4a47ed46b9..02969a071d 100644 --- a/packages/standard-components/src/DataFormWide.svelte +++ b/packages/standard-components/src/DataFormWide.svelte @@ -13,33 +13,39 @@ number: "number", } - let newModel = { - modelId: model, - } + let record let store = _bb.store let schema = {} let modelDef = {} let saved = false let saving = false + let recordId + let isNew = true let inputElements = {} $: if (model && model.length !== 0) { fetchModel() } + $: fields = Object.keys(schema) + + $: Object.values(inputElements).length && setForm(record) + async function fetchModel() { const FETCH_MODEL_URL = `/api/models/${model}` const response = await _bb.api.get(FETCH_MODEL_URL) modelDef = await response.json() schema = modelDef.schema } + async function save() { // prevent double clicking firing multiple requests if (saving) return saving = true const SAVE_RECORD_URL = `/api/${model}/records` - const response = await _bb.api.post(SAVE_RECORD_URL, newModel) + const response = await _bb.api.post(SAVE_RECORD_URL, record) + const json = await response.json() if (response.status === 200) { @@ -48,7 +54,13 @@ return state }) - resetForm() + // wipe form, if new record, otherwise update + // model to get new _rev + if (isNew) { + resetForm() + } else { + record = json + } // set saved, and unset after 1 second // i.e. make the success notifier appear, then disappear again after time @@ -69,26 +81,58 @@ el.checked = false } } - newModel = { + record = { modelId: model } } + const setForm = rec => { + if (isNew || !rec) return + for (let fieldName in inputElements) { + if (typeof rec[fieldName] === "boolean") { + inputElements[fieldName].checked = rec[fieldName] + } else { + inputElements[fieldName].value = rec[fieldName] + } + } + } + const handleInput = field => event => { let value + if (event.target.type === "checkbox") { value = event.target.checked - newModel[field] = value + record[field] = value return } + if (event.target.type === "number") { value = parseInt(event.target.value) - newModel[field] = value + record[field] = value return } + value = event.target.value - newModel[field] = value + record[field] = value } + + onMount(() => { + const routeParams = _bb.routeParams() + recordId = Object.keys(routeParams).length > 0 && (routeParams.id || routeParams[0]) + isNew = !recordId || recordId === "new" + + if (isNew) { + record = { modelId: model } + } else { + const GET_RECORD_URL = `/api/${model}/records/${recordId}` + _bb.api.get(GET_RECORD_URL) + .then(response => response.json()) + .then(rec => { + record = rec + setForm(rec) + }) + } + }); @@ -127,7 +171,7 @@ {buttonText || "Submit Form"} {/if} - +