From 3f7b22050a2ea563b31e58911553635b03ddd55c Mon Sep 17 00:00:00 2001 From: JulienM Date: Tue, 25 Feb 2020 18:17:07 +0100 Subject: [PATCH 01/19] Add custom submit function prop in simpleForm --- examples/simple/src/comments/CommentCreate.js | 9 ++++++++- packages/ra-core/src/form/FormWithRedirect.tsx | 8 +++++++- packages/ra-ui-materialui/src/form/SimpleForm.js | 2 ++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/examples/simple/src/comments/CommentCreate.js b/examples/simple/src/comments/CommentCreate.js index 730034527d..f10ec48f24 100644 --- a/examples/simple/src/comments/CommentCreate.js +++ b/examples/simple/src/comments/CommentCreate.js @@ -13,9 +13,16 @@ import PostReferenceInput from './PostReferenceInput'; const now = new Date(); const defaultSort = { field: 'title', order: 'ASC' }; +const handleSubmit = (values, redirect) => { + console.log('handleSubmit'); + console.log(values); + console.log(redirect); + return true; +}; + const CommentCreate = props => ( - + Date: Thu, 27 Feb 2020 11:41:14 +0100 Subject: [PATCH 02/19] Custom buttons --- examples/simple/src/comments/CommentCreate.js | 8 +++----- examples/simple/src/posts/PostCreate.js | 9 ++++++++- packages/ra-core/src/form/FormWithRedirect.tsx | 2 +- packages/ra-ui-materialui/src/form/SimpleForm.js | 12 +++++++++++- packages/ra-ui-materialui/src/form/Toolbar.js | 10 ++++++++++ 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/examples/simple/src/comments/CommentCreate.js b/examples/simple/src/comments/CommentCreate.js index f10ec48f24..206a7aea23 100644 --- a/examples/simple/src/comments/CommentCreate.js +++ b/examples/simple/src/comments/CommentCreate.js @@ -13,11 +13,9 @@ import PostReferenceInput from './PostReferenceInput'; const now = new Date(); const defaultSort = { field: 'title', order: 'ASC' }; -const handleSubmit = (values, redirect) => { - console.log('handleSubmit'); - console.log(values); - console.log(redirect); - return true; +const handleSubmit = (values, redirect, save) => { + console.log('Do something before default save function'); + save(values, redirect); }; const CommentCreate = props => ( diff --git a/examples/simple/src/posts/PostCreate.js b/examples/simple/src/posts/PostCreate.js index 279f9a0e96..3931054d4c 100644 --- a/examples/simple/src/posts/PostCreate.js +++ b/examples/simple/src/posts/PostCreate.js @@ -59,7 +59,7 @@ const SaveWithNoteButton = props => { basePath, ]); - return ; + return ; }; const PostCreateToolbar = props => ( @@ -90,6 +90,12 @@ const PostCreateToolbar = props => ( ); +const handleSubmit = (values, redirect) => { + console.log( + 'Disable default save function to have specific action on the Save buttons' + ); +}; + const backlinksDefaultValue = [ { date: new Date(), @@ -109,6 +115,7 @@ const PostCreate = ({ permissions, ...props }) => { return ( } initialValues={initialValues} validate={values => { diff --git a/packages/ra-core/src/form/FormWithRedirect.tsx b/packages/ra-core/src/form/FormWithRedirect.tsx index 35e8b71205..a987e8e106 100644 --- a/packages/ra-core/src/form/FormWithRedirect.tsx +++ b/packages/ra-core/src/form/FormWithRedirect.tsx @@ -72,7 +72,7 @@ const FormWithRedirect = ({ const finalValues = sanitizeEmptyValues(finalInitialValues, values); if (onSubmit && typeof onSubmit === 'function') { - onSubmit(finalValues, finalRedirect); + onSubmit(finalValues, finalRedirect, save); } else { save(finalValues, finalRedirect); } diff --git a/packages/ra-ui-materialui/src/form/SimpleForm.js b/packages/ra-ui-materialui/src/form/SimpleForm.js index 411e20299a..45b41cf4b9 100644 --- a/packages/ra-ui-materialui/src/form/SimpleForm.js +++ b/packages/ra-ui-materialui/src/form/SimpleForm.js @@ -44,7 +44,13 @@ import CardContentInner from '../layout/CardContentInner'; const SimpleForm = props => ( } + render={formProps => ( + + )} /> ); @@ -80,8 +86,10 @@ const SimpleFormView = ({ redirect, resource, saving, + save, submitOnEnter, toolbar, + onSubmit, undoable, variant, ...rest @@ -117,6 +125,8 @@ const SimpleFormView = ({ redirect, resource, saving, + save, + onSubmit, submitOnEnter, undoable, })} diff --git a/packages/ra-ui-materialui/src/form/Toolbar.js b/packages/ra-ui-materialui/src/form/Toolbar.js index 6c111c6fcd..f51ec2a607 100644 --- a/packages/ra-ui-materialui/src/form/Toolbar.js +++ b/packages/ra-ui-materialui/src/form/Toolbar.js @@ -59,12 +59,18 @@ const Toolbar = ({ redirect, resource, saving, + save, submitOnEnter, undoable, + onSubmit, width, ...rest }) => { const classes = useStyles({ classes: classesOverride }); + const onClick = + onSubmit && typeof onSubmit === 'function' && !invalid + ? save + : undefined; return ( Date: Thu, 27 Feb 2020 15:25:29 +0100 Subject: [PATCH 03/19] maybe ok --- .../ra-ui-materialui/src/button/SaveButton.tsx | 9 +++++++++ packages/ra-ui-materialui/src/form/Toolbar.js | 15 ++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index 8831d6d4bf..c98ade9235 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -5,6 +5,7 @@ import CircularProgress from '@material-ui/core/CircularProgress'; import { makeStyles } from '@material-ui/core/styles'; import ContentSave from '@material-ui/icons/Save'; import classnames from 'classnames'; +import { useFormState } from 'react-final-form'; import { useTranslate, useNotify, @@ -25,9 +26,11 @@ const SaveButton: FC = ({ icon = defaultIcon, onClick, handleSubmitWithRedirect, + selfSubmit, ...rest }) => { const classes = useStyles({ classes: classesOverride }); + const formState = useFormState(); const notify = useNotify(); const translate = useTranslate(); @@ -46,6 +49,10 @@ const SaveButton: FC = ({ handleSubmitWithRedirect(redirect); } + if (typeof selfSubmit === 'function') { + selfSubmit(formState.values, redirect); + } + if (typeof onClick === 'function') { onClick(event); } @@ -109,6 +116,7 @@ interface Props { classes?: object; className?: string; handleSubmitWithRedirect?: (redirect?: RedirectionSideEffect) => void; + selfSubmit?: (values: object, redirect: RedirectionSideEffect) => void; icon?: ReactElement; invalid?: boolean; label?: string; @@ -132,6 +140,7 @@ SaveButton.propTypes = { className: PropTypes.string, classes: PropTypes.object, handleSubmitWithRedirect: PropTypes.func, + selfSubmit: PropTypes.func, invalid: PropTypes.bool, label: PropTypes.string, pristine: PropTypes.bool, diff --git a/packages/ra-ui-materialui/src/form/Toolbar.js b/packages/ra-ui-materialui/src/form/Toolbar.js index f51ec2a607..3017baaa94 100644 --- a/packages/ra-ui-materialui/src/form/Toolbar.js +++ b/packages/ra-ui-materialui/src/form/Toolbar.js @@ -67,7 +67,7 @@ const Toolbar = ({ ...rest }) => { const classes = useStyles({ classes: classesOverride }); - const onClick = + const selfSubmit = onSubmit && typeof onSubmit === 'function' && !invalid ? save : undefined; @@ -106,8 +106,8 @@ const Toolbar = ({ )} ) : ( - Children.map(children, button => - button && isValidElement(button) + Children.map(children, button => { + return button && isValidElement(button) ? React.cloneElement(button, { basePath, handleSubmit: valueOrDefault( @@ -118,10 +118,7 @@ const Toolbar = ({ button.props.handleSubmitWithRedirect, handleSubmitWithRedirect ), - onClick: valueOrDefault( - button.props.onClick, - onClick - ), + selfSubmit, invalid, pristine, record, @@ -136,8 +133,8 @@ const Toolbar = ({ undoable ), }) - : null - ) + : null; + }) )}
From d921966c6e841766e5f14af47ec672376e400a56 Mon Sep 17 00:00:00 2001 From: JulienM Date: Thu, 27 Feb 2020 15:47:51 +0100 Subject: [PATCH 04/19] cut selfSubmit on custom submit --- examples/simple/src/posts/PostCreate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple/src/posts/PostCreate.js b/examples/simple/src/posts/PostCreate.js index 3931054d4c..2bdc3d71ad 100644 --- a/examples/simple/src/posts/PostCreate.js +++ b/examples/simple/src/posts/PostCreate.js @@ -22,7 +22,7 @@ import { } from 'react-admin'; // eslint-disable-line import/no-unresolved import { useFormState, FormSpy } from 'react-final-form'; -const SaveWithNoteButton = props => { +const SaveWithNoteButton = ({ selfSubmit, ...props }) => { const [create] = useCreate('posts'); const redirectTo = useRedirect(); const notify = useNotify(); From d227fcec30531c35c40664f1b06e9db808ccedb1 Mon Sep 17 00:00:00 2001 From: JulienM Date: Thu, 27 Feb 2020 18:18:03 +0100 Subject: [PATCH 05/19] refactor with setState --- examples/simple/src/comments/CommentCreate.js | 7 +-- examples/simple/src/posts/PostCreate.js | 61 +++++++------------ examples/simple/src/posts/PostEdit.js | 2 +- .../ra-core/src/form/FormWithRedirect.tsx | 16 ++--- .../src/button/SaveButton.tsx | 20 +++--- .../ra-ui-materialui/src/form/SimpleForm.js | 16 ++--- .../ra-ui-materialui/src/form/TabbedForm.js | 10 ++- packages/ra-ui-materialui/src/form/Toolbar.js | 14 +++-- 8 files changed, 67 insertions(+), 79 deletions(-) diff --git a/examples/simple/src/comments/CommentCreate.js b/examples/simple/src/comments/CommentCreate.js index 206a7aea23..730034527d 100644 --- a/examples/simple/src/comments/CommentCreate.js +++ b/examples/simple/src/comments/CommentCreate.js @@ -13,14 +13,9 @@ import PostReferenceInput from './PostReferenceInput'; const now = new Date(); const defaultSort = { field: 'title', order: 'ASC' }; -const handleSubmit = (values, redirect, save) => { - console.log('Do something before default save function'); - save(values, redirect); -}; - const CommentCreate = props => ( - + { const [create] = useCreate('posts'); const redirectTo = useRedirect(); const notify = useNotify(); - const { basePath, redirect } = props; + const { basePath } = props; - const formState = useFormState(); - const handleClick = useCallback(() => { - if (!formState.valid) { - return; - } - - create( - { - payload: { - data: { ...formState.values, average_note: 10 }, - }, - }, - { - onSuccess: ({ data: newRecord }) => { - notify('ra.notification.created', 'info', { - smart_count: 1, - }); - redirectTo(redirect, basePath, newRecord.id, newRecord); + const handleClick = useCallback( + (values, redirect) => { + create( + { + payload: { + data: { ...values, average_note: 10 }, + }, }, - } - ); - }, [ - formState.valid, - formState.values, - create, - notify, - redirectTo, - redirect, - basePath, - ]); + { + onSuccess: ({ data: newRecord }) => { + notify('ra.notification.created', 'info', { + smart_count: 1, + }); + redirectTo(redirect, basePath, newRecord.id, newRecord); + }, + } + ); + }, + [create, notify, redirectTo, basePath] + ); - return ; + return ; }; const PostCreateToolbar = props => ( @@ -90,12 +80,6 @@ const PostCreateToolbar = props => ( ); -const handleSubmit = (values, redirect) => { - console.log( - 'Disable default save function to have specific action on the Save buttons' - ); -}; - const backlinksDefaultValue = [ { date: new Date(), @@ -115,7 +99,6 @@ const PostCreate = ({ permissions, ...props }) => { return ( } initialValues={initialValues} validate={values => { diff --git a/examples/simple/src/posts/PostEdit.js b/examples/simple/src/posts/PostEdit.js index 13341fabf7..c97042246b 100644 --- a/examples/simple/src/posts/PostEdit.js +++ b/examples/simple/src/posts/PostEdit.js @@ -46,7 +46,7 @@ const EditActions = ({ basePath, data, hasShow }) => ( const PostEdit = ({ permissions, ...props }) => ( } actions={} {...props}> - + diff --git a/packages/ra-core/src/form/FormWithRedirect.tsx b/packages/ra-core/src/form/FormWithRedirect.tsx index a987e8e106..b469ca0213 100644 --- a/packages/ra-core/src/form/FormWithRedirect.tsx +++ b/packages/ra-core/src/form/FormWithRedirect.tsx @@ -24,7 +24,7 @@ import getFormInitialValues from './getFormInitialValues'; * @typedef {object} Props the props you can use (other props are injected by Create or Edit) * @prop {object} initialValues * @prop {function} validate - * @prop {function} onSubmit + * @prop {function} save * @prop {boolean} submitOnEnter * @prop {string} redirect * @@ -45,12 +45,12 @@ const FormWithRedirect = ({ saving, subscription = defaultSubscription, validate, - onSubmit, validateOnBlur, version, ...props }) => { let redirect = useRef(props.redirect); + let realSave = useRef(save); // We don't use state here for two reasons: // 1. There no way to execute code only after the state has been updated // 2. We don't want the form to rerender when redirect is changed @@ -58,6 +58,10 @@ const FormWithRedirect = ({ redirect.current = newRedirect; }; + const setSave = newSave => { + realSave.current = newSave; + }; + const finalInitialValues = getFormInitialValues( initialValues, defaultValue, @@ -71,11 +75,7 @@ const FormWithRedirect = ({ : redirect.current; const finalValues = sanitizeEmptyValues(finalInitialValues, values); - if (onSubmit && typeof onSubmit === 'function') { - onSubmit(finalValues, finalRedirect, save); - } else { - save(finalValues, finalRedirect); - } + realSave.current(finalValues, finalRedirect); }; return ( @@ -101,6 +101,8 @@ const FormWithRedirect = ({ setRedirect={setRedirect} saving={formProps.submitting || saving} render={render} + save={save} + setSave={setSave} /> )} diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index c98ade9235..2cb5531273 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -5,7 +5,6 @@ import CircularProgress from '@material-ui/core/CircularProgress'; import { makeStyles } from '@material-ui/core/styles'; import ContentSave from '@material-ui/icons/Save'; import classnames from 'classnames'; -import { useFormState } from 'react-final-form'; import { useTranslate, useNotify, @@ -26,15 +25,18 @@ const SaveButton: FC = ({ icon = defaultIcon, onClick, handleSubmitWithRedirect, - selfSubmit, + customSave, + setSave, ...rest }) => { const classes = useStyles({ classes: classesOverride }); - const formState = useFormState(); const notify = useNotify(); const translate = useTranslate(); const handleClick = event => { + if (typeof setSave === 'function') { + setSave(customSave); + } if (saving) { // prevent double submission event.preventDefault(); @@ -49,10 +51,6 @@ const SaveButton: FC = ({ handleSubmitWithRedirect(redirect); } - if (typeof selfSubmit === 'function') { - selfSubmit(formState.values, redirect); - } - if (typeof onClick === 'function') { onClick(event); } @@ -116,7 +114,10 @@ interface Props { classes?: object; className?: string; handleSubmitWithRedirect?: (redirect?: RedirectionSideEffect) => void; - selfSubmit?: (values: object, redirect: RedirectionSideEffect) => void; + customSave?: (values: object, redirect: RedirectionSideEffect) => void; + setSave?: ( + save: (values: object, redirect: RedirectionSideEffect) => void + ) => void; icon?: ReactElement; invalid?: boolean; label?: string; @@ -140,7 +141,8 @@ SaveButton.propTypes = { className: PropTypes.string, classes: PropTypes.object, handleSubmitWithRedirect: PropTypes.func, - selfSubmit: PropTypes.func, + customSave: PropTypes.func, + setSave: PropTypes.func, invalid: PropTypes.bool, label: PropTypes.string, pristine: PropTypes.bool, diff --git a/packages/ra-ui-materialui/src/form/SimpleForm.js b/packages/ra-ui-materialui/src/form/SimpleForm.js index 45b41cf4b9..ff403f88fd 100644 --- a/packages/ra-ui-materialui/src/form/SimpleForm.js +++ b/packages/ra-ui-materialui/src/form/SimpleForm.js @@ -45,11 +45,7 @@ const SimpleForm = props => ( ( - + )} /> ); @@ -69,7 +65,6 @@ SimpleForm.propTypes = { submitOnEnter: PropTypes.bool, undoable: PropTypes.bool, validate: PropTypes.func, - onSubmit: PropTypes.func, version: PropTypes.number, }; @@ -86,10 +81,10 @@ const SimpleFormView = ({ redirect, resource, saving, - save, submitOnEnter, toolbar, - onSubmit, + save, + setSave, undoable, variant, ...rest @@ -126,7 +121,7 @@ const SimpleFormView = ({ resource, saving, save, - onSubmit, + setSave, submitOnEnter, undoable, })} @@ -148,6 +143,7 @@ SimpleFormView.propTypes = { PropTypes.func, ]), save: PropTypes.func, // the handler defined in the parent, which triggers the REST submission + setSave: PropTypes.func, saving: PropTypes.bool, submitOnEnter: PropTypes.bool, toolbar: PropTypes.element, @@ -192,6 +188,7 @@ const sanitizeRestProps = ({ reset, resetSection, save, + setSave, setRedirect, submit, submitError, @@ -207,7 +204,6 @@ const sanitizeRestProps = ({ untouch, valid, validate, - onSubmit, validating, _reduxForm, ...props diff --git a/packages/ra-ui-materialui/src/form/TabbedForm.js b/packages/ra-ui-materialui/src/form/TabbedForm.js index 5c6389cb01..053453858c 100644 --- a/packages/ra-ui-materialui/src/form/TabbedForm.js +++ b/packages/ra-ui-materialui/src/form/TabbedForm.js @@ -79,7 +79,9 @@ import TabbedFormTabs, { getTabFullPath } from './TabbedFormTabs'; const TabbedForm = props => ( } + render={formProps => ( + + )} /> ); @@ -130,6 +132,8 @@ export const TabbedFormView = ({ submitOnEnter, tabs, toolbar, + save, + setSave, translate, undoable, value, @@ -204,6 +208,8 @@ export const TabbedFormView = ({ redirect: defaultRedirect, resource, saving, + save, + setSave, submitOnEnter, undoable, })} @@ -231,6 +237,7 @@ TabbedFormView.propTypes = { ]), resource: PropTypes.string, save: PropTypes.func, // the handler defined in the parent, which triggers the REST submission + setSave: PropTypes.func, saving: PropTypes.bool, submitOnEnter: PropTypes.bool, tabs: PropTypes.element.isRequired, @@ -281,6 +288,7 @@ const sanitizeRestProps = ({ reset, resetSection, save, + setSave, staticContext, submit, submitAsSideEffect, diff --git a/packages/ra-ui-materialui/src/form/Toolbar.js b/packages/ra-ui-materialui/src/form/Toolbar.js index 3017baaa94..d9b8271f51 100644 --- a/packages/ra-ui-materialui/src/form/Toolbar.js +++ b/packages/ra-ui-materialui/src/form/Toolbar.js @@ -62,15 +62,11 @@ const Toolbar = ({ save, submitOnEnter, undoable, - onSubmit, + setSave, width, ...rest }) => { const classes = useStyles({ classes: classesOverride }); - const selfSubmit = - onSubmit && typeof onSubmit === 'function' && !invalid - ? save - : undefined; return ( {record && typeof record.id !== 'undefined' && ( @@ -118,7 +116,11 @@ const Toolbar = ({ button.props.handleSubmitWithRedirect, handleSubmitWithRedirect ), - selfSubmit, + customSave: valueOrDefault( + button.props.customSave, + save + ), + setSave, invalid, pristine, record, From a3f5f8e98b4fbf801f6d360d9ae83832a874dbaf Mon Sep 17 00:00:00 2001 From: JulienM Date: Thu, 27 Feb 2020 18:27:31 +0100 Subject: [PATCH 06/19] remove unused --- examples/simple/src/posts/PostCreate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple/src/posts/PostCreate.js b/examples/simple/src/posts/PostCreate.js index ffa40859af..9d5df63194 100644 --- a/examples/simple/src/posts/PostCreate.js +++ b/examples/simple/src/posts/PostCreate.js @@ -22,7 +22,7 @@ import { } from 'react-admin'; // eslint-disable-line import/no-unresolved import { FormSpy } from 'react-final-form'; -const SaveWithNoteButton = ({ selfSubmit, ...props }) => { +const SaveWithNoteButton = props => { const [create] = useCreate('posts'); const redirectTo = useRedirect(); const notify = useNotify(); From 5f7ea1bdaa1d2b0df19d5422436afab853850628 Mon Sep 17 00:00:00 2001 From: JulienM Date: Thu, 27 Feb 2020 18:34:13 +0100 Subject: [PATCH 07/19] clean --- packages/ra-ui-materialui/src/form/Toolbar.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ra-ui-materialui/src/form/Toolbar.js b/packages/ra-ui-materialui/src/form/Toolbar.js index d9b8271f51..bf8d6fba25 100644 --- a/packages/ra-ui-materialui/src/form/Toolbar.js +++ b/packages/ra-ui-materialui/src/form/Toolbar.js @@ -104,8 +104,8 @@ const Toolbar = ({ )}
) : ( - Children.map(children, button => { - return button && isValidElement(button) + Children.map(children, button => + button && isValidElement(button) ? React.cloneElement(button, { basePath, handleSubmit: valueOrDefault( @@ -135,8 +135,8 @@ const Toolbar = ({ undoable ), }) - : null; - }) + : null + ) )}
From 59b6506a6fb7872ff9bc7b06b3b407ca9bdb81cd Mon Sep 17 00:00:00 2001 From: JulienM Date: Mon, 2 Mar 2020 15:51:57 +0100 Subject: [PATCH 08/19] review --- examples/simple/src/posts/PostCreate.js | 4 ++-- packages/ra-core/src/form/FormWithRedirect.tsx | 10 +++++----- .../ra-ui-materialui/src/button/SaveButton.tsx | 18 +++++++++--------- .../ra-ui-materialui/src/form/SimpleForm.js | 8 ++++---- .../ra-ui-materialui/src/form/TabbedForm.js | 8 ++++---- packages/ra-ui-materialui/src/form/Toolbar.js | 12 ++++++------ 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/examples/simple/src/posts/PostCreate.js b/examples/simple/src/posts/PostCreate.js index 9d5df63194..862c53faaa 100644 --- a/examples/simple/src/posts/PostCreate.js +++ b/examples/simple/src/posts/PostCreate.js @@ -28,7 +28,7 @@ const SaveWithNoteButton = props => { const notify = useNotify(); const { basePath } = props; - const handleClick = useCallback( + const handleSave = useCallback( (values, redirect) => { create( { @@ -49,7 +49,7 @@ const SaveWithNoteButton = props => { [create, notify, redirectTo, basePath] ); - return ; + return ; }; const PostCreateToolbar = props => ( diff --git a/packages/ra-core/src/form/FormWithRedirect.tsx b/packages/ra-core/src/form/FormWithRedirect.tsx index b469ca0213..003a9bfe65 100644 --- a/packages/ra-core/src/form/FormWithRedirect.tsx +++ b/packages/ra-core/src/form/FormWithRedirect.tsx @@ -50,7 +50,7 @@ const FormWithRedirect = ({ ...props }) => { let redirect = useRef(props.redirect); - let realSave = useRef(save); + let onSave = useRef(save); // We don't use state here for two reasons: // 1. There no way to execute code only after the state has been updated // 2. We don't want the form to rerender when redirect is changed @@ -58,8 +58,8 @@ const FormWithRedirect = ({ redirect.current = newRedirect; }; - const setSave = newSave => { - realSave.current = newSave; + const setOnSave = newOnSave => { + onSave.current = newOnSave; }; const finalInitialValues = getFormInitialValues( @@ -75,7 +75,7 @@ const FormWithRedirect = ({ : redirect.current; const finalValues = sanitizeEmptyValues(finalInitialValues, values); - realSave.current(finalValues, finalRedirect); + onSave.current(finalValues, finalRedirect); }; return ( @@ -102,7 +102,7 @@ const FormWithRedirect = ({ saving={formProps.submitting || saving} render={render} save={save} - setSave={setSave} + setOnSave={setOnSave} /> )} diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index 2cb5531273..18e44a131f 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -25,8 +25,8 @@ const SaveButton: FC = ({ icon = defaultIcon, onClick, handleSubmitWithRedirect, - customSave, - setSave, + onSave, + setOnSave, ...rest }) => { const classes = useStyles({ classes: classesOverride }); @@ -34,8 +34,8 @@ const SaveButton: FC = ({ const translate = useTranslate(); const handleClick = event => { - if (typeof setSave === 'function') { - setSave(customSave); + if (typeof setOnSave === 'function') { + setOnSave(onSave); } if (saving) { // prevent double submission @@ -114,9 +114,9 @@ interface Props { classes?: object; className?: string; handleSubmitWithRedirect?: (redirect?: RedirectionSideEffect) => void; - customSave?: (values: object, redirect: RedirectionSideEffect) => void; - setSave?: ( - save: (values: object, redirect: RedirectionSideEffect) => void + onSave?: (values: object, redirect: RedirectionSideEffect) => void; + setOnSave?: ( + onSave: (values: object, redirect: RedirectionSideEffect) => void ) => void; icon?: ReactElement; invalid?: boolean; @@ -141,8 +141,8 @@ SaveButton.propTypes = { className: PropTypes.string, classes: PropTypes.object, handleSubmitWithRedirect: PropTypes.func, - customSave: PropTypes.func, - setSave: PropTypes.func, + onSave: PropTypes.func, + setOnSave: PropTypes.func, invalid: PropTypes.bool, label: PropTypes.string, pristine: PropTypes.bool, diff --git a/packages/ra-ui-materialui/src/form/SimpleForm.js b/packages/ra-ui-materialui/src/form/SimpleForm.js index ff403f88fd..4467cb03b4 100644 --- a/packages/ra-ui-materialui/src/form/SimpleForm.js +++ b/packages/ra-ui-materialui/src/form/SimpleForm.js @@ -84,7 +84,7 @@ const SimpleFormView = ({ submitOnEnter, toolbar, save, - setSave, + setOnSave, undoable, variant, ...rest @@ -121,7 +121,7 @@ const SimpleFormView = ({ resource, saving, save, - setSave, + setOnSave, submitOnEnter, undoable, })} @@ -143,7 +143,7 @@ SimpleFormView.propTypes = { PropTypes.func, ]), save: PropTypes.func, // the handler defined in the parent, which triggers the REST submission - setSave: PropTypes.func, + setOnSave: PropTypes.func, saving: PropTypes.bool, submitOnEnter: PropTypes.bool, toolbar: PropTypes.element, @@ -188,7 +188,7 @@ const sanitizeRestProps = ({ reset, resetSection, save, - setSave, + setOnSave, setRedirect, submit, submitError, diff --git a/packages/ra-ui-materialui/src/form/TabbedForm.js b/packages/ra-ui-materialui/src/form/TabbedForm.js index 053453858c..784044f3c0 100644 --- a/packages/ra-ui-materialui/src/form/TabbedForm.js +++ b/packages/ra-ui-materialui/src/form/TabbedForm.js @@ -133,7 +133,7 @@ export const TabbedFormView = ({ tabs, toolbar, save, - setSave, + setOnSave, translate, undoable, value, @@ -209,7 +209,7 @@ export const TabbedFormView = ({ resource, saving, save, - setSave, + setOnSave, submitOnEnter, undoable, })} @@ -237,7 +237,7 @@ TabbedFormView.propTypes = { ]), resource: PropTypes.string, save: PropTypes.func, // the handler defined in the parent, which triggers the REST submission - setSave: PropTypes.func, + setOnSave: PropTypes.func, saving: PropTypes.bool, submitOnEnter: PropTypes.bool, tabs: PropTypes.element.isRequired, @@ -288,7 +288,7 @@ const sanitizeRestProps = ({ reset, resetSection, save, - setSave, + setOnSave, staticContext, submit, submitAsSideEffect, diff --git a/packages/ra-ui-materialui/src/form/Toolbar.js b/packages/ra-ui-materialui/src/form/Toolbar.js index bf8d6fba25..1ac497a245 100644 --- a/packages/ra-ui-materialui/src/form/Toolbar.js +++ b/packages/ra-ui-materialui/src/form/Toolbar.js @@ -62,7 +62,7 @@ const Toolbar = ({ save, submitOnEnter, undoable, - setSave, + setOnSave, width, ...rest }) => { @@ -90,8 +90,8 @@ const Toolbar = ({ invalid={invalid} redirect={redirect} saving={saving} - customSave={save} - setSave={setSave} + save={save} + setOnSave={setOnSave} submitOnEnter={submitOnEnter} /> {record && typeof record.id !== 'undefined' && ( @@ -116,11 +116,11 @@ const Toolbar = ({ button.props.handleSubmitWithRedirect, handleSubmitWithRedirect ), - customSave: valueOrDefault( - button.props.customSave, + onSave: valueOrDefault( + button.props.onSave, save ), - setSave, + setOnSave, invalid, pristine, record, From d3b0bee75c385c5c1aecde143bf8b5a7019df4ab Mon Sep 17 00:00:00 2001 From: JulienM Date: Mon, 2 Mar 2020 16:32:39 +0100 Subject: [PATCH 09/19] fix test --- packages/ra-ui-materialui/src/form/Toolbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/form/Toolbar.js b/packages/ra-ui-materialui/src/form/Toolbar.js index 1ac497a245..48c7b1bb54 100644 --- a/packages/ra-ui-materialui/src/form/Toolbar.js +++ b/packages/ra-ui-materialui/src/form/Toolbar.js @@ -90,7 +90,7 @@ const Toolbar = ({ invalid={invalid} redirect={redirect} saving={saving} - save={save} + onSave={save} setOnSave={setOnSave} submitOnEnter={submitOnEnter} /> From 2ec23b5eb214a26c92eb850b8957a8acaf7acd1f Mon Sep 17 00:00:00 2001 From: JulienM Date: Mon, 2 Mar 2020 17:53:56 +0100 Subject: [PATCH 10/19] fix rebase --- packages/ra-ui-materialui/src/button/SaveButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index 18e44a131f..e79f9303b5 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -33,7 +33,7 @@ const SaveButton: FC = ({ const notify = useNotify(); const translate = useTranslate(); - const handleClick = event => { + const handleMouseDown = event => { if (typeof setOnSave === 'function') { setOnSave(onSave); } From 5f31f96798543f55f1cb696eae66651063f71aac Mon Sep 17 00:00:00 2001 From: JulienM Date: Wed, 4 Mar 2020 10:49:21 +0100 Subject: [PATCH 11/19] update docs --- UPGRADE.md | 80 ++++++++++---------- docs/CreateEdit.md | 180 +++++++++++++++++++++++++++++---------------- 2 files changed, 154 insertions(+), 106 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index 7a0f560ffb..b56647f294 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -103,7 +103,7 @@ The author of `redux-form` has written a new Form library for React called `reac The next sections highlight changes that you must do to your code as a consequence of switching to `react-final-form`. -## Custom Form Toolbar or Buttons Must Use New `handleSubmit` Signature +## Custom Form Toolbar or Buttons Must Use New `handleSubmit` Signature or must Use `onSave` If you were using custom buttons (to alter the form values before submit for example), you'll need to update your code. In `react-admin` v2, the form toolbar and its buttons used to receive `handleSubmit` and `handleSubmitWithRedirect` props. These props accepted functions which were called with the form values. @@ -112,9 +112,7 @@ The migration to `react-final-form` changes their signature and behavior to the - `handleSubmit`: accepts no arguments, and will submit the form with its current values immediately - `handleSubmitWithRedirect` accepts a custom redirect, and will submit the form with its current values immediately -Here's how to migrate the *Altering the Form Values before Submitting* example from the documentation, in two variants: - -1. Using the `react-final-form` hook API to send change events +Here's how to migrate the *Altering the Form Values before Submitting* example from the documentation: ```jsx import React, { useCallback } from 'react'; @@ -139,53 +137,51 @@ const SaveWithNoteButton = ({ handleSubmit, handleSubmitWithRedirect, ...props } }; ``` -2. Using react-admin hooks to run custom mutations +The override of these functions has now a huge drawback, which makes it impractical: by skipping the default `handleSubmitWithRedirect`, the button doesn't trigger form validation. And unfortunately, react-final-form doesn't provide a way to trigger form validation manually. +​ +That's why react-admin now provides a way to override just the data provider call and its side effect called `onSave`. + +The `onSave` value should be a function expecting 2 arguments: the form values to save, and the redirection to perform. -For instance, in the `simple` example: +Here's how to migrate the *Using `onSave` To Alter the Form Submission Behavior* example from the documentation: ```jsx import React, { useCallback } from 'react'; -import { useFormState } from 'react-final-form'; -import { SaveButton, Toolbar, useCreate, useRedirect, useNotify } from 'react-admin'; - +import { + SaveButton, + Toolbar, + useCreate, + useRedirect, + useNotify, +} from 'react-admin'; +​ const SaveWithNoteButton = props => { const [create] = useCreate('posts'); const redirectTo = useRedirect(); const notify = useNotify(); - const { basePath, redirect } = props; - - const formState = useFormState(); - const handleClick = useCallback(() => { - if (!formState.valid) { - return; - } - - create( - { - payload: { - data: { ...formState.values, average_note: 10 }, + const { basePath } = props; +​ + const handleSave = useCallback( + (values, redirect) => { + create( + { + payload: { data: { ...values, average_note: 10 } }, }, - }, - { - onSuccess: ({ data: newRecord }) => { - notify('ra.notification.created', 'info', { - smart_count: 1, - }); - redirectTo(redirect, basePath, newRecord.id, newRecord); - }, - } - ); - }, [ - formState.valid, - formState.values, - create, - notify, - redirectTo, - redirect, - basePath, - ]); - - return ; + { + onSuccess: ({ data: newRecord }) => { + notify('ra.notification.created', 'info', { + smart_count: 1, + }); + redirectTo(redirect, basePath, newRecord.id, newRecord); + }, + } + ); + }, + [create, notify, redirectTo, basePath] + ); +​ + // set onSave props instead of handleSubmitWithRedirect + return ; }; ``` diff --git a/docs/CreateEdit.md b/docs/CreateEdit.md index 1c8432273f..bcdf2d66c2 100644 --- a/docs/CreateEdit.md +++ b/docs/CreateEdit.md @@ -1230,85 +1230,42 @@ export const UserEdit = ({ permissions, ...props }) => ``` {% endraw %} -## Altering the Form Values before Submitting +## Altering the Form Values Before Submitting -Sometimes, you may want your custom action to alter the form values before actually sending them to the `dataProvider`. -For those cases, you should know that every button inside a form [Toolbar](#toolbar) receive two props: +Sometimes, you may want to alter the form values before actually sending them to the `dataProvider`. For those cases, you should know that every button inside a form [Toolbar](#toolbar) receive two props: -- `handleSubmit` which calls the default form save method -- `handleSubmitWithRedirect` which calls the default form save method but allows to specify a custom redirection - -Knowing this, there are two ways to alter the form values before submit: - -1. Using react-final-form API to send change events +* `handleSubmit` which calls the default form save method (provided by react-final-form) +* `handleSubmitWithRedirect` which calls the default form save method and allows to specify a custom redirection +​ +Decorating `handleSubmitWithRedirect` with your own logic allows you to alter the form values before submitting. For instance, to set the `average_note` field value just before submission: ```jsx import React, { useCallback } from 'react'; import { useForm } from 'react-final-form'; -import { SaveButton, Toolbar, useCreate, useRedirect, useNotify } from 'react-admin'; - +import { + SaveButton, + Toolbar, + useCreate, + useRedirect, + useNotify, +} from 'react-admin'; +​ const SaveWithNoteButton = ({ handleSubmitWithRedirect, ...props }) => { const [create] = useCreate('posts'); const redirectTo = useRedirect(); const notify = useNotify(); const { basePath, redirect } = props; - +​ const form = useForm(); - +​ const handleClick = useCallback(() => { + // change the average_note field value form.change('average_note', 10); - +​ handleSubmitWithRedirect('edit'); }, [form]); - - return ; -}; -``` - -2. Using react-admin hooks to run custom mutations - -For instance, in the `simple` example: - -```jsx -import React, { useCallback } from 'react'; -import { useFormState } from 'react-final-form'; -import { SaveButton, Toolbar, useCreate, useRedirect, useNotify } from 'react-admin'; - -const SaveWithNoteButton = props => { - const [create] = useCreate('posts'); - const redirectTo = useRedirect(); - const notify = useNotify(); - const { basePath, redirect } = props; - - const formState = useFormState(); - const handleClick = useCallback(() => { - if (!formState.valid) { - return; - } - - create( - { - payload: { data: { ...formState.values, average_note: 10 } }, - }, - { - onSuccess: ({ data: newRecord }) => { - notify('ra.notification.created', 'info', { - smart_count: 1, - }); - redirectTo(redirect, basePath, newRecord.id, newRecord); - }, - } - ); - }, [ - formState.valid, - formState.values, - create, - notify, - redirectTo, - redirect, - basePath, - ]); - +​ + // override handleSubmitWithRedirect with custom logic return ; }; ``` @@ -1333,4 +1290,99 @@ const PostCreateToolbar = props => ( ); ``` -**Note**: This technique will not trigger a form validation pass. +**Tip**: Which one of `handleSubmit` and `handleSubmitWithRedirect` should you override? If you want to keep the redirection, override `handleSubmitWithRedirect` just like in the previous example. If you want to disable redirection, or handle it yourself, override `handleSubmit`. + +## Using `onSave` To Alter the Form Submission Behavior + +The previous technique works well for altering values. But you may want to call a route before submission, or submit the form to different dataProvider methods/resources depending on the form values. And in this case, wrapping `handleSubmitWithRedirect` does not work, because you don't have control on the submission itself. +​ +Instead of *decorating* `handleSubmitWithRedirect`, you can *replace* it, and do the API call manually. You don't have to change anything in the form values in that case. So the previous example can be rewritten as: + +```jsx +import React, { useCallback } from 'react'; +import { useFormState } from 'react-final-form'; +import { + SaveButton, + Toolbar, + useCreate, + useRedirect, + useNotify, +} from 'react-admin'; +​ +const SaveWithNoteButton = props => { + const [create] = useCreate('posts'); + const redirectTo = useRedirect(); + const notify = useNotify(); + const { basePath, redirect } = props; + // get values from the form + const formState = useFormState(); +​ + const handleClick = useCallback( + () => { + // call dataProvider.create() manually + create( + { + payload: { data: { ...formState.values, average_note: 10 } }, + }, + { + onSuccess: ({ data: newRecord }) => { + notify('ra.notification.created', 'info', { + smart_count: 1, + }); + redirectTo(redirect, basePath, newRecord.id, newRecord); + }, + } + ); + }, + [create, notify, redirectTo, basePath, formState, redirect] + ); +​ + return ; +}; +``` + +This technique has a huge drawback, which makes it impractical: by skipping the default `handleSubmitWithRedirect`, this button doesn't trigger form validation. And unfortunately, react-final-form doesn't provide a way to trigger form validation manually. +​ +That's why react-admin provides a way to override just the data provider call and its side effects. It's called `onSave`, and here is how you would use it in the previous use case: + +```jsx +import React, { useCallback } from 'react'; +import { + SaveButton, + Toolbar, + useCreate, + useRedirect, + useNotify, +} from 'react-admin'; +​ +const SaveWithNoteButton = props => { + const [create] = useCreate('posts'); + const redirectTo = useRedirect(); + const notify = useNotify(); + const { basePath } = props; +​ + const handleSave = useCallback( + (values, redirect) => { + create( + { + payload: { data: { ...values, average_note: 10 } }, + }, + { + onSuccess: ({ data: newRecord }) => { + notify('ra.notification.created', 'info', { + smart_count: 1, + }); + redirectTo(redirect, basePath, newRecord.id, newRecord); + }, + } + ); + }, + [create, notify, redirectTo, basePath] + ); +​ + // set onSave props instead of handleSubmitWithRedirect + return ; +}; +``` + +The `onSave` value should be a function expecting 2 arguments: the form values to save, and the redirection to perform. From feedc1f763a10faf8f9c2823e1fd0324b49a9432 Mon Sep 17 00:00:00 2001 From: JulienM Date: Wed, 4 Mar 2020 10:51:21 +0100 Subject: [PATCH 12/19] fix next rebase --- packages/ra-ui-materialui/src/button/SaveButton.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index e79f9303b5..c2206a4c25 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -33,6 +33,11 @@ const SaveButton: FC = ({ const notify = useNotify(); const translate = useTranslate(); + // We handle the click event through mousedown because of an issue when + // the button is not as the same place when mouseup occurs, preventing the click + // event to fire. + // It can happen when some errors appear under inputs, pushing the button + // towards the window bottom. const handleMouseDown = event => { if (typeof setOnSave === 'function') { setOnSave(onSave); From c457b10469d0866af4bc1edd2fc786c45abf7a19 Mon Sep 17 00:00:00 2001 From: JulienM Date: Wed, 4 Mar 2020 13:50:14 +0100 Subject: [PATCH 13/19] use context form changing save --- .../ra-core/src/form/FormWithRedirect.tsx | 57 ++++++++++--------- packages/ra-core/src/form/OnSaveContext.tsx | 10 ++++ packages/ra-core/src/form/index.ts | 2 + packages/ra-core/src/types.ts | 4 ++ .../src/button/SaveButton.tsx | 17 +++--- .../ra-ui-materialui/src/form/SimpleForm.js | 4 -- .../ra-ui-materialui/src/form/TabbedForm.js | 4 -- packages/ra-ui-materialui/src/form/Toolbar.js | 3 - yarn.lock | 11 +++- 9 files changed, 65 insertions(+), 47 deletions(-) create mode 100644 packages/ra-core/src/form/OnSaveContext.tsx diff --git a/packages/ra-core/src/form/FormWithRedirect.tsx b/packages/ra-core/src/form/FormWithRedirect.tsx index 003a9bfe65..0b34039115 100644 --- a/packages/ra-core/src/form/FormWithRedirect.tsx +++ b/packages/ra-core/src/form/FormWithRedirect.tsx @@ -5,6 +5,7 @@ import arrayMutators from 'final-form-arrays'; import useInitializeFormWithRecord from './useInitializeFormWithRecord'; import sanitizeEmptyValues from './sanitizeEmptyValues'; import getFormInitialValues from './getFormInitialValues'; +import OnSaveContext from './OnSaveContext'; /** * Wrapper around react-final-form's Form to handle redirection on submit, @@ -51,6 +52,7 @@ const FormWithRedirect = ({ }) => { let redirect = useRef(props.redirect); let onSave = useRef(save); + // We don't use state here for two reasons: // 1. There no way to execute code only after the state has been updated // 2. We don't want the form to rerender when redirect is changed @@ -79,33 +81,34 @@ const FormWithRedirect = ({ }; return ( -
- {formProps => ( - - )} - + +
+ {formProps => ( + + )} + +
); }; diff --git a/packages/ra-core/src/form/OnSaveContext.tsx b/packages/ra-core/src/form/OnSaveContext.tsx new file mode 100644 index 0000000000..b5a0374f61 --- /dev/null +++ b/packages/ra-core/src/form/OnSaveContext.tsx @@ -0,0 +1,10 @@ +import React, { createContext } from 'react'; +import { SetOnSave } from '../types'; + +const defaultSetOnSave: SetOnSave = onSave => {}; + +const OnSaveContext = createContext(defaultSetOnSave); + +OnSaveContext.displayName = 'OnSaveContext'; + +export default OnSaveContext; diff --git a/packages/ra-core/src/form/index.ts b/packages/ra-core/src/form/index.ts index 2fa0f1c68b..eb21ba3207 100644 --- a/packages/ra-core/src/form/index.ts +++ b/packages/ra-core/src/form/index.ts @@ -1,5 +1,6 @@ import addField from './addField'; import FormDataConsumer from './FormDataConsumer'; +import OnSaveContext from './OnSaveContext'; import FormField from './FormField'; import FormWithRedirect from './FormWithRedirect'; import useInput, { InputProps } from './useInput'; @@ -28,6 +29,7 @@ export { useInitializeFormWithRecord, useSuggestions, ValidationError, + OnSaveContext, }; export { isRequired } from './FormField'; export * from './validate'; diff --git a/packages/ra-core/src/types.ts b/packages/ra-core/src/types.ts index 326c844fcb..b678c3860e 100644 --- a/packages/ra-core/src/types.ts +++ b/packages/ra-core/src/types.ts @@ -428,3 +428,7 @@ export type Exporter = ( dataProvider: DataProvider, resource?: string ) => Promise; + +export type SetOnSave = ( + onSave: (values: object, redirect: any) => void +) => void; diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index c2206a4c25..4a5c8274ff 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -1,4 +1,10 @@ -import React, { cloneElement, FC, ReactElement, SyntheticEvent } from 'react'; +import React, { + useContext, + cloneElement, + FC, + ReactElement, + SyntheticEvent, +} from 'react'; import PropTypes from 'prop-types'; import Button, { ButtonProps } from '@material-ui/core/Button'; import CircularProgress from '@material-ui/core/CircularProgress'; @@ -10,6 +16,7 @@ import { useNotify, RedirectionSideEffect, Record, + OnSaveContext, } from 'ra-core'; const SaveButton: FC = ({ @@ -26,12 +33,12 @@ const SaveButton: FC = ({ onClick, handleSubmitWithRedirect, onSave, - setOnSave, ...rest }) => { const classes = useStyles({ classes: classesOverride }); const notify = useNotify(); const translate = useTranslate(); + const setOnSave = useContext(OnSaveContext); // We handle the click event through mousedown because of an issue when // the button is not as the same place when mouseup occurs, preventing the click @@ -39,7 +46,7 @@ const SaveButton: FC = ({ // It can happen when some errors appear under inputs, pushing the button // towards the window bottom. const handleMouseDown = event => { - if (typeof setOnSave === 'function') { + if (typeof onSave === 'function') { setOnSave(onSave); } if (saving) { @@ -120,9 +127,6 @@ interface Props { className?: string; handleSubmitWithRedirect?: (redirect?: RedirectionSideEffect) => void; onSave?: (values: object, redirect: RedirectionSideEffect) => void; - setOnSave?: ( - onSave: (values: object, redirect: RedirectionSideEffect) => void - ) => void; icon?: ReactElement; invalid?: boolean; label?: string; @@ -147,7 +151,6 @@ SaveButton.propTypes = { classes: PropTypes.object, handleSubmitWithRedirect: PropTypes.func, onSave: PropTypes.func, - setOnSave: PropTypes.func, invalid: PropTypes.bool, label: PropTypes.string, pristine: PropTypes.bool, diff --git a/packages/ra-ui-materialui/src/form/SimpleForm.js b/packages/ra-ui-materialui/src/form/SimpleForm.js index 4467cb03b4..4ea94f3f6c 100644 --- a/packages/ra-ui-materialui/src/form/SimpleForm.js +++ b/packages/ra-ui-materialui/src/form/SimpleForm.js @@ -84,7 +84,6 @@ const SimpleFormView = ({ submitOnEnter, toolbar, save, - setOnSave, undoable, variant, ...rest @@ -121,7 +120,6 @@ const SimpleFormView = ({ resource, saving, save, - setOnSave, submitOnEnter, undoable, })} @@ -143,7 +141,6 @@ SimpleFormView.propTypes = { PropTypes.func, ]), save: PropTypes.func, // the handler defined in the parent, which triggers the REST submission - setOnSave: PropTypes.func, saving: PropTypes.bool, submitOnEnter: PropTypes.bool, toolbar: PropTypes.element, @@ -188,7 +185,6 @@ const sanitizeRestProps = ({ reset, resetSection, save, - setOnSave, setRedirect, submit, submitError, diff --git a/packages/ra-ui-materialui/src/form/TabbedForm.js b/packages/ra-ui-materialui/src/form/TabbedForm.js index 784044f3c0..eb1dadde04 100644 --- a/packages/ra-ui-materialui/src/form/TabbedForm.js +++ b/packages/ra-ui-materialui/src/form/TabbedForm.js @@ -133,7 +133,6 @@ export const TabbedFormView = ({ tabs, toolbar, save, - setOnSave, translate, undoable, value, @@ -209,7 +208,6 @@ export const TabbedFormView = ({ resource, saving, save, - setOnSave, submitOnEnter, undoable, })} @@ -237,7 +235,6 @@ TabbedFormView.propTypes = { ]), resource: PropTypes.string, save: PropTypes.func, // the handler defined in the parent, which triggers the REST submission - setOnSave: PropTypes.func, saving: PropTypes.bool, submitOnEnter: PropTypes.bool, tabs: PropTypes.element.isRequired, @@ -288,7 +285,6 @@ const sanitizeRestProps = ({ reset, resetSection, save, - setOnSave, staticContext, submit, submitAsSideEffect, diff --git a/packages/ra-ui-materialui/src/form/Toolbar.js b/packages/ra-ui-materialui/src/form/Toolbar.js index 48c7b1bb54..40ea6eef4a 100644 --- a/packages/ra-ui-materialui/src/form/Toolbar.js +++ b/packages/ra-ui-materialui/src/form/Toolbar.js @@ -62,7 +62,6 @@ const Toolbar = ({ save, submitOnEnter, undoable, - setOnSave, width, ...rest }) => { @@ -91,7 +90,6 @@ const Toolbar = ({ redirect={redirect} saving={saving} onSave={save} - setOnSave={setOnSave} submitOnEnter={submitOnEnter} /> {record && typeof record.id !== 'undefined' && ( @@ -120,7 +118,6 @@ const Toolbar = ({ button.props.onSave, save ), - setOnSave, invalid, pristine, record, diff --git a/yarn.lock b/yarn.lock index 7071856ee9..6bab8ca008 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1579,7 +1579,7 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/prop-types@*": +"@types/prop-types@*", "@types/prop-types@^15.6.0": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== @@ -1594,6 +1594,13 @@ resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-5.1.0.tgz#7f40cdea49ddafa0ea4f3db35fb6c24d3bfd4dcc" integrity sha512-9/sJK+T04pNq7uwReR0CLxqXj1dhxiTapZ1tIxA0trEsT6FRS0bz09YMcMb7tsVBTm4RJ0NEBYGsAjoEmqoFXg== +"@types/quill@~1.3.0": + version "1.3.10" + resolved "https://registry.yarnpkg.com/@types/quill/-/quill-1.3.10.tgz#dc1f7b6587f7ee94bdf5291bc92289f6f0497613" + integrity sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw== + dependencies: + parchment "^1.1.2" + "@types/react-dom@^16.9.4": version "16.9.5" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7" @@ -11729,7 +11736,7 @@ param-case@2.1.x, param-case@^2.1.1: dependencies: no-case "^2.2.0" -parchment@^1.1.4: +parchment@^1.1.2, parchment@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5" integrity sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg== From 89644b3245086aecf11e16b1295e1c14793afc22 Mon Sep 17 00:00:00 2001 From: JulienM Date: Wed, 4 Mar 2020 14:14:38 +0100 Subject: [PATCH 14/19] simplify with context --- packages/ra-core/src/form/FormWithRedirect.tsx | 4 +++- packages/ra-core/src/form/OnSaveContext.tsx | 2 +- packages/ra-core/src/types.ts | 2 +- packages/ra-ui-materialui/src/button/SaveButton.tsx | 2 ++ packages/ra-ui-materialui/src/form/SimpleForm.js | 6 +----- packages/ra-ui-materialui/src/form/TabbedForm.js | 6 +----- packages/ra-ui-materialui/src/form/Toolbar.js | 7 +------ 7 files changed, 10 insertions(+), 19 deletions(-) diff --git a/packages/ra-core/src/form/FormWithRedirect.tsx b/packages/ra-core/src/form/FormWithRedirect.tsx index 0b34039115..4ab51fda97 100644 --- a/packages/ra-core/src/form/FormWithRedirect.tsx +++ b/packages/ra-core/src/form/FormWithRedirect.tsx @@ -61,7 +61,9 @@ const FormWithRedirect = ({ }; const setOnSave = newOnSave => { - onSave.current = newOnSave; + typeof newOnSave === 'function' + ? (onSave.current = newOnSave) + : (onSave.current = save); }; const finalInitialValues = getFormInitialValues( diff --git a/packages/ra-core/src/form/OnSaveContext.tsx b/packages/ra-core/src/form/OnSaveContext.tsx index b5a0374f61..5d2d9a021f 100644 --- a/packages/ra-core/src/form/OnSaveContext.tsx +++ b/packages/ra-core/src/form/OnSaveContext.tsx @@ -1,7 +1,7 @@ import React, { createContext } from 'react'; import { SetOnSave } from '../types'; -const defaultSetOnSave: SetOnSave = onSave => {}; +const defaultSetOnSave: SetOnSave = () => {}; const OnSaveContext = createContext(defaultSetOnSave); diff --git a/packages/ra-core/src/types.ts b/packages/ra-core/src/types.ts index b678c3860e..ceeacc2d9a 100644 --- a/packages/ra-core/src/types.ts +++ b/packages/ra-core/src/types.ts @@ -430,5 +430,5 @@ export type Exporter = ( ) => Promise; export type SetOnSave = ( - onSave: (values: object, redirect: any) => void + onSave?: (values: object, redirect: any) => void ) => void; diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index 4a5c8274ff..11071a0c5b 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -48,6 +48,8 @@ const SaveButton: FC = ({ const handleMouseDown = event => { if (typeof onSave === 'function') { setOnSave(onSave); + } else { + setOnSave(); } if (saving) { // prevent double submission diff --git a/packages/ra-ui-materialui/src/form/SimpleForm.js b/packages/ra-ui-materialui/src/form/SimpleForm.js index 4ea94f3f6c..e668ab0232 100644 --- a/packages/ra-ui-materialui/src/form/SimpleForm.js +++ b/packages/ra-ui-materialui/src/form/SimpleForm.js @@ -44,9 +44,7 @@ import CardContentInner from '../layout/CardContentInner'; const SimpleForm = props => ( ( - - )} + render={formProps => } /> ); @@ -83,7 +81,6 @@ const SimpleFormView = ({ saving, submitOnEnter, toolbar, - save, undoable, variant, ...rest @@ -119,7 +116,6 @@ const SimpleFormView = ({ redirect, resource, saving, - save, submitOnEnter, undoable, })} diff --git a/packages/ra-ui-materialui/src/form/TabbedForm.js b/packages/ra-ui-materialui/src/form/TabbedForm.js index eb1dadde04..5c6389cb01 100644 --- a/packages/ra-ui-materialui/src/form/TabbedForm.js +++ b/packages/ra-ui-materialui/src/form/TabbedForm.js @@ -79,9 +79,7 @@ import TabbedFormTabs, { getTabFullPath } from './TabbedFormTabs'; const TabbedForm = props => ( ( - - )} + render={formProps => } /> ); @@ -132,7 +130,6 @@ export const TabbedFormView = ({ submitOnEnter, tabs, toolbar, - save, translate, undoable, value, @@ -207,7 +204,6 @@ export const TabbedFormView = ({ redirect: defaultRedirect, resource, saving, - save, submitOnEnter, undoable, })} diff --git a/packages/ra-ui-materialui/src/form/Toolbar.js b/packages/ra-ui-materialui/src/form/Toolbar.js index 40ea6eef4a..490b2ed8f5 100644 --- a/packages/ra-ui-materialui/src/form/Toolbar.js +++ b/packages/ra-ui-materialui/src/form/Toolbar.js @@ -59,7 +59,6 @@ const Toolbar = ({ redirect, resource, saving, - save, submitOnEnter, undoable, width, @@ -89,7 +88,6 @@ const Toolbar = ({ invalid={invalid} redirect={redirect} saving={saving} - onSave={save} submitOnEnter={submitOnEnter} /> {record && typeof record.id !== 'undefined' && ( @@ -114,10 +112,7 @@ const Toolbar = ({ button.props.handleSubmitWithRedirect, handleSubmitWithRedirect ), - onSave: valueOrDefault( - button.props.onSave, - save - ), + onSave: button.props.onSave, invalid, pristine, record, From 10759b577473872637fb3d86cb17868005dddede Mon Sep 17 00:00:00 2001 From: JulienM Date: Thu, 5 Mar 2020 09:40:14 +0100 Subject: [PATCH 15/19] Rebase next --- packages/ra-ui-materialui/src/button/SaveButton.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index 11071a0c5b..63a40be5b4 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -40,12 +40,7 @@ const SaveButton: FC = ({ const translate = useTranslate(); const setOnSave = useContext(OnSaveContext); - // We handle the click event through mousedown because of an issue when - // the button is not as the same place when mouseup occurs, preventing the click - // event to fire. - // It can happen when some errors appear under inputs, pushing the button - // towards the window bottom. - const handleMouseDown = event => { + const handleClick = event => { if (typeof onSave === 'function') { setOnSave(onSave); } else { From 5bccd4770deb86e65c18df91fd3ced7e83e34df1 Mon Sep 17 00:00:00 2001 From: JulienM Date: Thu, 5 Mar 2020 10:27:48 +0100 Subject: [PATCH 16/19] more global context --- packages/ra-core/src/form/FormContext.tsx | 10 ++++++++++ packages/ra-core/src/form/FormWithRedirect.tsx | 6 +++--- packages/ra-core/src/form/OnSaveContext.tsx | 10 ---------- packages/ra-core/src/form/index.ts | 4 ++-- packages/ra-core/src/types.ts | 4 ++++ packages/ra-ui-materialui/src/button/SaveButton.tsx | 8 ++++---- 6 files changed, 23 insertions(+), 19 deletions(-) create mode 100644 packages/ra-core/src/form/FormContext.tsx delete mode 100644 packages/ra-core/src/form/OnSaveContext.tsx diff --git a/packages/ra-core/src/form/FormContext.tsx b/packages/ra-core/src/form/FormContext.tsx new file mode 100644 index 0000000000..9379095441 --- /dev/null +++ b/packages/ra-core/src/form/FormContext.tsx @@ -0,0 +1,10 @@ +import React, { createContext } from 'react'; +import { FormFunctions } from '../types'; + +const defaultFormFunctions: FormFunctions = { setOnSave: () => {} }; + +const FormContext = createContext(defaultFormFunctions); + +FormContext.displayName = 'FormContext'; + +export default FormContext; diff --git a/packages/ra-core/src/form/FormWithRedirect.tsx b/packages/ra-core/src/form/FormWithRedirect.tsx index 4ab51fda97..96b04173bd 100644 --- a/packages/ra-core/src/form/FormWithRedirect.tsx +++ b/packages/ra-core/src/form/FormWithRedirect.tsx @@ -5,7 +5,7 @@ import arrayMutators from 'final-form-arrays'; import useInitializeFormWithRecord from './useInitializeFormWithRecord'; import sanitizeEmptyValues from './sanitizeEmptyValues'; import getFormInitialValues from './getFormInitialValues'; -import OnSaveContext from './OnSaveContext'; +import FormContext from './FormContext'; /** * Wrapper around react-final-form's Form to handle redirection on submit, @@ -83,7 +83,7 @@ const FormWithRedirect = ({ }; return ( - +
)}
-
+ ); }; diff --git a/packages/ra-core/src/form/OnSaveContext.tsx b/packages/ra-core/src/form/OnSaveContext.tsx deleted file mode 100644 index 5d2d9a021f..0000000000 --- a/packages/ra-core/src/form/OnSaveContext.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React, { createContext } from 'react'; -import { SetOnSave } from '../types'; - -const defaultSetOnSave: SetOnSave = () => {}; - -const OnSaveContext = createContext(defaultSetOnSave); - -OnSaveContext.displayName = 'OnSaveContext'; - -export default OnSaveContext; diff --git a/packages/ra-core/src/form/index.ts b/packages/ra-core/src/form/index.ts index eb21ba3207..3d2f5fdcac 100644 --- a/packages/ra-core/src/form/index.ts +++ b/packages/ra-core/src/form/index.ts @@ -1,6 +1,6 @@ import addField from './addField'; import FormDataConsumer from './FormDataConsumer'; -import OnSaveContext from './OnSaveContext'; +import FormContext from './FormContext'; import FormField from './FormField'; import FormWithRedirect from './FormWithRedirect'; import useInput, { InputProps } from './useInput'; @@ -29,7 +29,7 @@ export { useInitializeFormWithRecord, useSuggestions, ValidationError, - OnSaveContext, + FormContext, }; export { isRequired } from './FormField'; export * from './validate'; diff --git a/packages/ra-core/src/types.ts b/packages/ra-core/src/types.ts index ceeacc2d9a..61a9caca3d 100644 --- a/packages/ra-core/src/types.ts +++ b/packages/ra-core/src/types.ts @@ -432,3 +432,7 @@ export type Exporter = ( export type SetOnSave = ( onSave?: (values: object, redirect: any) => void ) => void; + +export type FormFunctions = { + setOnSave?: SetOnSave; +}; diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index 63a40be5b4..1d1e5c409a 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -16,7 +16,7 @@ import { useNotify, RedirectionSideEffect, Record, - OnSaveContext, + FormContext, } from 'ra-core'; const SaveButton: FC = ({ @@ -38,13 +38,13 @@ const SaveButton: FC = ({ const classes = useStyles({ classes: classesOverride }); const notify = useNotify(); const translate = useTranslate(); - const setOnSave = useContext(OnSaveContext); + const formContext = useContext(FormContext); const handleClick = event => { if (typeof onSave === 'function') { - setOnSave(onSave); + formContext.setOnSave(onSave); } else { - setOnSave(); + formContext.setOnSave(); } if (saving) { // prevent double submission From 1bf460dce72f3a16539d54d2a058d8465501850c Mon Sep 17 00:00:00 2001 From: JulienM Date: Thu, 5 Mar 2020 10:43:54 +0100 Subject: [PATCH 17/19] Revert yarn.lock --- yarn.lock | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6bab8ca008..7071856ee9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1579,7 +1579,7 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/prop-types@*", "@types/prop-types@^15.6.0": +"@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== @@ -1594,13 +1594,6 @@ resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-5.1.0.tgz#7f40cdea49ddafa0ea4f3db35fb6c24d3bfd4dcc" integrity sha512-9/sJK+T04pNq7uwReR0CLxqXj1dhxiTapZ1tIxA0trEsT6FRS0bz09YMcMb7tsVBTm4RJ0NEBYGsAjoEmqoFXg== -"@types/quill@~1.3.0": - version "1.3.10" - resolved "https://registry.yarnpkg.com/@types/quill/-/quill-1.3.10.tgz#dc1f7b6587f7ee94bdf5291bc92289f6f0497613" - integrity sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw== - dependencies: - parchment "^1.1.2" - "@types/react-dom@^16.9.4": version "16.9.5" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7" @@ -11736,7 +11729,7 @@ param-case@2.1.x, param-case@^2.1.1: dependencies: no-case "^2.2.0" -parchment@^1.1.2, parchment@^1.1.4: +parchment@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5" integrity sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg== From da3413674c94f2d65b659f6b5c889d1377b13a3c Mon Sep 17 00:00:00 2001 From: JulienM Date: Thu, 5 Mar 2020 12:01:09 +0100 Subject: [PATCH 18/19] useMemo --- packages/ra-core/src/form/FormWithRedirect.tsx | 6 ++++-- packages/ra-ui-materialui/src/button/SaveButton.tsx | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/ra-core/src/form/FormWithRedirect.tsx b/packages/ra-core/src/form/FormWithRedirect.tsx index 96b04173bd..8fbd65ed21 100644 --- a/packages/ra-core/src/form/FormWithRedirect.tsx +++ b/packages/ra-core/src/form/FormWithRedirect.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useCallback } from 'react'; +import React, { useRef, useCallback, useMemo } from 'react'; import { Form } from 'react-final-form'; import arrayMutators from 'final-form-arrays'; @@ -66,6 +66,8 @@ const FormWithRedirect = ({ : (onSave.current = save); }; + const formContextValue = useMemo(() => ({ setOnSave }), [setOnSave]); + const finalInitialValues = getFormInitialValues( initialValues, defaultValue, @@ -83,7 +85,7 @@ const FormWithRedirect = ({ }; return ( - +
= ({ const classes = useStyles({ classes: classesOverride }); const notify = useNotify(); const translate = useTranslate(); - const formContext = useContext(FormContext); + const { setOnSave } = useContext(FormContext); const handleClick = event => { if (typeof onSave === 'function') { - formContext.setOnSave(onSave); + setOnSave(onSave); } else { - formContext.setOnSave(); + setOnSave(); } if (saving) { // prevent double submission From c55b4009d2898f1b66d4c15782ef68dde15704ff Mon Sep 17 00:00:00 2001 From: JulienM Date: Thu, 5 Mar 2020 14:30:08 +0100 Subject: [PATCH 19/19] Comment setOnSave behaviour --- packages/ra-core/src/form/FormWithRedirect.tsx | 9 +++++++++ packages/ra-ui-materialui/src/button/SaveButton.tsx | 1 + 2 files changed, 10 insertions(+) diff --git a/packages/ra-core/src/form/FormWithRedirect.tsx b/packages/ra-core/src/form/FormWithRedirect.tsx index 8fbd65ed21..c69e80e789 100644 --- a/packages/ra-core/src/form/FormWithRedirect.tsx +++ b/packages/ra-core/src/form/FormWithRedirect.tsx @@ -60,6 +60,15 @@ const FormWithRedirect = ({ redirect.current = newRedirect; }; + /** + * A form can have several Save buttons. In case the user clicks on + * a Save button with a custom onSave handler, then on a second Save button + * without custom onSave handler, the user expects the default save + * handler (the one of the Form) to be called. + * That's why the SaveButton onClick calls setOnSave() with no parameters + * if it has no custom onSave, and why this function forces a default to + * save. + */ const setOnSave = newOnSave => { typeof newOnSave === 'function' ? (onSave.current = newOnSave) diff --git a/packages/ra-ui-materialui/src/button/SaveButton.tsx b/packages/ra-ui-materialui/src/button/SaveButton.tsx index 2c027ef414..cdb5199137 100644 --- a/packages/ra-ui-materialui/src/button/SaveButton.tsx +++ b/packages/ra-ui-materialui/src/button/SaveButton.tsx @@ -44,6 +44,7 @@ const SaveButton: FC = ({ if (typeof onSave === 'function') { setOnSave(onSave); } else { + // we reset to the Form default save function setOnSave(); } if (saving) {