Add ResourceEditionForm to edit an entity
authorFrank Bessou <frank.bessou@logilab.fr>
Thu, 11 May 2017 10:26:57 +0200
changeset 196 b30b72c99142
parent 195 179d532e517a
child 197 926f820c4216
Add ResourceEditionForm to edit an entity
src/components/Entity.js
src/components/Resource.js
src/index.js
--- a/src/components/Entity.js	Thu May 11 10:56:48 2017 +0200
+++ b/src/components/Entity.js	Thu May 11 10:26:57 2017 +0200
@@ -2,13 +2,9 @@
 import {PropTypes} from 'prop-types';
 import {FormWrapper} from './Form';
 
-import {merge} from 'lodash/object';
-
 import Api from '../Api';
 import HypermediaClient from '../services/hypermedia';
-import {mapToSchema, PropTypeJsonaryWrapper} from '../jsonaryutils';
-import {PropTypesEntityModel} from '../model';
-import {buildFormData, appendPath} from '../utils';
+import {PropTypeJsonaryWrapper} from '../jsonaryutils';
 import {CollectionView} from './BaseViews';
 import {AttributeValue} from './Attribute';
 
@@ -143,91 +139,6 @@
     link: PropTypes.object.isRequired,
 };
 
-export class Entity extends React.Component {
-
-    constructor(props) {
-        super(props);
-        this.state = this.getInitialState(props);
-        this.getEditionSchema = this.getEditionSchema.bind(this);
-        this.updateEntity = this.updateEntity.bind(this);
-    }
-
-    getInitialState() {
-        return {entity: null};
-    }
-
-    getEType() {
-        return this.props.match.params.etype;
-    }
-
-    getEID() {
-        return this.props.match.params.eid;
-    }
-
-    componentDidMount() {
-        this.initEntity(this.getEType(), this.getEID());
-    }
-
-    componentWillReceiveProps(nextProps) {
-        this.setState(this.getInitialState(nextProps));
-        const etype = nextProps.match.params.etype;
-        const eid = nextProps.match.params.eid;
-        this.initEntity(etype, eid);
-    }
-
-    initEntity(etype, eid) {
-        Api.getEntity(etype, eid)
-            .then(
-                (entity) => {
-                    this.setState({entity: entity});
-                }
-            );
-    }
-
-    updateEntity(instance) {
-        const entity = mapToSchema(instance, this.state.entity.data.schemas()[0].data.value());
-        this.setState(merge({}, this.state, {entity: {data: entity}}));
-    }
-
-    getEditionSchema() {
-        return HypermediaClient.getSchema(appendPath(this.state.entity.url, '/schema?role=edition'));
-    }
-
-    render() {
-        if (this.state.entity === null) {
-            return <div>loading...</div>;
-        }
-        if (this.props.match.params.view === 'edit') {
-            const etype = this.getEType();
-            const eid = this.getEID();
-            const redirectPath = `/${etype}/${eid}`;
-            return (
-                <EntityEditForm {...this.props}
-                    entity={this.state.entity}
-                    getSchema={this.getEditionSchema}
-                    updateEntity={this.updateEntity}
-                    redirectPath={ redirectPath }
-                />
-            );
-        }
-    }
-
-}
-
-Entity.propTypes = {
-    match: PropTypes.shape({
-        params: PropTypes.shape({
-            etype: PropTypes.string.isRequired,
-            eid: PropTypes.string.isRequired,
-            view: PropTypes.string,
-        }),
-    }),
-};
-
-Entity.contextTypes = {
-    router: PropTypes.object.isRequired,
-};
-
 const uiSchema = {
     description: {
         'ui:widget': 'textarea',
@@ -315,35 +226,3 @@
         }).isRequired,
     }),
 };
-
-export class EntityEditForm extends EntityForm {
-
-    componentDidMount() {
-        this.props.getSchema()
-            .then(
-                (schema) => {
-                    const {data} = this.props.entity;
-                    const entity = mapToSchema(data.value(), schema);
-                    const formData = buildFormData(entity);
-                    this.setState({schema: schema, formData: formData});
-                }
-            );
-    }
-
-    onSubmit({formData}) {
-        const url = this.props.entity.route;
-        HypermediaClient.updateResource(url, formData)
-            .then(entity => {
-                this.props.updateEntity(entity);
-                this.context.router.history.push(this.props.redirectPath);
-            });
-    }
-
-}
-
-EntityEditForm.propTypes = {
-    entity: PropTypesEntityModel.isRequired,
-    redirectPath: PropTypes.string.isRequired,
-    getSchema: PropTypes.func.isRequired,
-    updateEntity: PropTypes.func.isRequired,
-};
--- a/src/components/Resource.js	Thu May 11 10:56:48 2017 +0200
+++ b/src/components/Resource.js	Thu May 11 10:26:57 2017 +0200
@@ -3,7 +3,7 @@
 import {PropTypes} from 'prop-types';
 import {merge} from 'lodash/object';
 
-import {appendPath} from '../utils';
+import {appendPath, buildFormData} from '../utils';
 import {EntityAttributes, EntityForm, EntityMeta, RelatedResources} from './Entity';
 import {PropTypesResourceModel} from '../model';
 import {PropTypeJsonaryWrapper, mapToSchema} from '../jsonaryutils';
@@ -212,6 +212,62 @@
     router: PropTypes.object.isRequired,
 };
 
+export class ResourceEditionForm extends React.Component {
+
+    constructor(props) {
+        super(props);
+        this.state = {};
+        this.onSubmit = this.onSubmit.bind(this);
+    }
+
+    componentDidMount() {
+        return hypermediaClient.getSchema(appendPath(this.props.resource.url, '/schema?role=edition'))
+            .then(
+                (schema) => {
+                    const {data} = this.props.resource;
+                    const entity = mapToSchema(data.value(), schema);
+                    const formData = buildFormData(entity);
+                    this.setState({schema: schema, formData: formData});
+                }
+            );
+    }
+
+    onSubmit({formData}) {
+        const url = this.props.resource.url;
+        hypermediaClient.updateResource(url, formData)
+            .then(data => {
+                if (data.hasOwnProperty('errors')) {
+                    // XXX better update formData to `.addError` inline...
+                    this.setState({errors: data.errors});
+                    return;
+                }
+                this.props.updateResource(data);
+                this.context.router.history.push(url);
+            });
+    }
+
+    render() {
+        if (!this.state.schema) {
+            return <div>Loading...</div>;
+        }
+        return (
+            <EntityForm
+                onSubmit={this.onSubmit}
+                schema={this.state.schema}
+                formData={this.state.formData}
+            />
+        );
+    }
+
+}
+ResourceEditionForm.propTypes = {
+    resource: PropTypesResourceModel.isRequired,
+    updateResource: PropTypes.func.isRequired,
+};
+ResourceEditionForm.contextTypes = {
+    router: PropTypes.object.isRequired,
+};
+
 export function Resource(props) {
     function selectView() {
         switch (props.match.params.action) {
@@ -219,6 +275,8 @@
                 return ResourceCreationForm;
             case 'delete':
                 return ResourceDeletionView;
+            case 'edit':
+                return ResourceEditionForm;
             case 'view':
             default:
                 return ResourceView;
--- a/src/index.js	Thu May 11 10:56:48 2017 +0200
+++ b/src/index.js	Thu May 11 10:26:57 2017 +0200
@@ -4,7 +4,7 @@
 
 import {App, NotFound} from './components/App';
 import {Root} from './components/Root';
-import {Entity, AddRelated} from './components/Entity';
+import {AddRelated} from './components/Entity';
 import {Resource} from './components/Resource';
 
 import "script-loader!jsonary/super-bundle/jsonary-super-bundle.js";
@@ -18,11 +18,7 @@
                 <Route exact path='/' component={Root} />
                 <Route path="/:etype/:eid/relationships/:rtype" component={AddRelated} />
 
-                {/* Entity edit route */}
-                <Route exact path="/:etype/:eid@@:view(edit)" component={Entity} />
-                <Route exact path="/:etype/:eid/@@:view(edit)" component={Entity} />
-
-                <Route exact path=":apiUrl(.*)@@:action(view|new|delete)" component={Resource} />
+                <Route exact path=":apiUrl(.*)@@:action(view|new|delete|edit)" component={Resource} />
                 <Route exact path=":apiUrl(.*)" component={Resource} />
 
                 <Route path="*" component={NotFound} />