react-crud-admin is inspired by the Django Admin Interface. In the spirit of Django admin it opts for component customization by inheritance. The create-read-update-delete pattern is something that is encountered in a lot of apps. By creating a single component that lists entries and allows adding/changing it becomes possible to implement a DRY approach.
Since React is primarily a UI library and there are a litany of backends, react-crud-admin does not implement any backend specific features but allows the developer to provide custom implementations.
Install the library,
npm install react-crud-admin
If you are starting a react project you can use create-react-app to speed up bootstrapping,
npm install create-react-app
npx create-react-app example
cd example
then within the example
directory,
npm install react-crud-admin
Create a new file example.js
in the \src
folder, in that file add the following lines
import React from 'react';
import Admin from "react-crud-admin";
export default class Example extends Admin
{
constructor()
{
super()
this.name='Contact'; // name of the objects
this.name_plural='Contacts'; // name of the objects in plural
this.list_display_links=['name']; // which property of the object is clickable
this.list_display=['name','number','address.street']// a list of properties of the object to displayed on the list display page
}
}
get_queryset()
{
// the actual array containing objects to be displayed
return [
{id: 1, name: 'Joe Next', number: '08939303003',address:{ street: "Hallmark Street"}},
{id: 2,name: 'Isa Yoll', number: '0908839202',address:{ street: "Barbican Street"}}
]
}
}
then in index.js
do
import React from 'react';
import ReactDOM from 'react-dom';
import Example from './example.js';
ReactDOM.render(<Example/>
, document.querySelector("#app"))
The output in your browser should be
This is the list display view. At this point adding and editing objects will not be possible since we have not configured the add/change view. To do that we need to import the react-jsonschema-form
package.
Edit example.js
and add the following,
import React from 'react';
import Admin from "./admin.js";
import Form from "react-jsonschema-form";
export default class Example extends Admin
{
constructor()
{
super()
this.name='Contact';
this.name_plural='Contacts';
this.list_display_links=['name'];
this.list_display=['name','number','address.street']
}
get_queryset()
{
return [
{id: 1, name: 'Ken Next', number: '08939303003',address:{ street: "Hallmark Street"}},
{id: 2,name: 'Isa Yoll', number: '0908839202',address:{ street: "Barbican Street"}}
]
}
get_form(object=null)
{
let schema = {
title: this.name,
type: "object",
required: ["name"],
properties: {
id: {type: "number", title: "id", default: Math.floor(1000*Math.random())+1 },
name: {type: "string", title: "Name", default: ""},
number : {type: "string", title: "Number", default: ""},
address : {type: "object", title: "Address",
properties : {
street : { type : "string",title : "Street"}
}
}
}
};
if(!object)
{
return <Form schema={schema} />
}
else
{
return <Form schema={schema} formData={object} />
}
}
}
the get_form
method is passed the current object. The method returns a react-json-schema
Form
component. The schema
object is used to define a schema
for the Form (and to provide validation). See more on react-jsonschema-form
at react-jsonschema-form. You can also read more on JSON Schema at JSON Schema Tutorial
In get_form
we check if an object exists displaying a preloaded form if it does and an empty one if it does not.
The output in your browser after clicking the Add Contact button should be
The output in your browser after clicking the first contact’s name should be
Data is primarily injected through get_queryset
.
get_queryset()
{
return [
{id: 1, name: 'Ken Next', number: '08939303003',address:{ street: "Hallmark Street"}},
{id: 2,name: 'Isa Yoll', number: '0908839202',address:{ street: "Barbican Street"}}
]
}
The get_queryset
method returns the queryset array including objects to be displayed. Returning an array may not be useful when asynchronous network calls are made to a remote backend using AJAX or window.fetch
. A class method of the Admin
component set_queryset
can be used for asynchronous calls.
The example below returns the queryset in the current state object synchronously and sets a new queryset when the asynchronous call returns successfully. set_queryset
invokes setState
internally.
get_queryset()
{
fetch("/path/to/backend",{method : 'get'}).then((response)=>{
if(response.ok)
{
this.set_queryset(response.results);
}
})
return this.state.queryset;
}
Table headers in the list display view are obtained from property names in get_list_display
method by default. There are certain cases when one would like to customise these headers. get_header_transforms
does just that. As an example
get_header_transforms()
{
return { 'name' : function(name)
{
return 'Contact '+name
}
}
}
Should produce
Header transforms can be defined using the instance variable header_transforms
(which is always overridden by get_header_transforms
).
this.header_transforms = { 'name' : function(name)
{
return 'Contact '+name
}
}
In summary get_header_transforms
returns an object whose properties are field names corresponding to properties of any object in the queryset and whose values are transform functions.
Field transforms return an object whose properties are field names corresponding to properties of any object in the queryset and whose values are transform functions. They are used to transform the values of objects within the queryset. For example to transform every “name” property of all the objects in the queryset to lower case we use,
get_field_transforms()
{
return { 'name' : function(content,object)
{
return content.toLowerCase()
}
}
}
or
javascript
this.field_transforms ={ 'name' : function(content,object)
{
return content.toLowerCase()
}
}
This produces
All the contact names have been turned to lower case. Field transforms apply to full columns on the data table in the display view.
It is sometimes desirable to create new fields that are not properties in any of the objects in the queryset. As an example, we create a new field that displays the current time using the moment
library.
```javascript
get_extra_fields() { return { ‘now’ : function(object,label) { return moment(new Date()).format(‘LLL’); } } }
or somewhere in the constructor,
this.extra_fields ={ ‘now’ : function(object,label) { return moment(new Date()).format(‘LLL’); } }
The `get_extra_fields` method returns an object whose properties are extra field names not corresponding to properties of any object in the queryset and whose values are display functions. This will create extra fields that are not tied to objects. Extra fields have to be manually included in the `get_list_display` in order to appear in the list display page.
Adding the `get_extra_fields` method is not enough to display the newly created field. We must add the field to `get_list_display`.
this.list_display=[‘name’,‘number’,‘address.street’,‘now’]
```
Don’t forget to install moment with npm install moment --save
and add import moment from "moment"
at the top of the file. The output is,