Binding React-Admin ReferenceInput with SelectInput to render on DOM - react-admin

I have two endpoints
api/instruction
api/subject
from my server, model Instructions has a reference field called subject.
On my react-admin frontend, I'd like to CREATE(CRUD) a new Instruction instance.
Following this tutorial I have implemented my InstructionCreate as below
export const InstructionCreate = (props) => (
<Create title="New Instruction"{...props}>
<SimpleForm>
<ReferenceInput label="Subject" source="subject" reference="subject/">
<SelectInput optionText="name" />
</ReferenceInput>
</SimpleForm>
</Create>
When I render my Create component, from chrome console, under networktab, I can see a list of subject objects returned.
The list has two objects(pulled from server) and the objects have a property 'name'
However, i get a console error
Uncaught TypeError: Cannot read property 'data' of undefined
The above error occurred in the
In case one needs my app.js, from which I can successfully CRUD the api/subject endpoint
const App = () => (
<Admin
dataProvider={dataProvider}
<Resource name="subject" title="Subjects" list={SubjectList} create={SubjectCreate} edit={SubjectEdit} icon={GroupIcon}/>
<Resource name="instruction" title="Instructions" list={InstructionList} edit={InstructionEdit} create={InstructionCreate} icon={InstructionIcon}/>
</Admin>
);
GET api/subject - returns a list of dictionaries
[
{
"id": 2,
"name": "Subject 2"
},
{
"id": 1,
"name": "Subject 1"
}
]

I was able to solve this.
The error was on my side for including a trailing '/' on the reference var
Changed this line
<ReferenceInput label="Subject" source="subject" reference="subject/">
to
<ReferenceInput label="Subject" source="subject" reference="subject">

Related

How to create a custom record action button inside a List component with React-Admin?

I'm a totally newbie with React and React-Admin. IMHO, I'm trying to achieve something simple that many people must have already done but I cannot find any kind of tutorial anywhere.
I'd like to add another button to the list of action buttons (show/edit) within each row in a <List> component. This button would archive the record.
My last try looks like the code below.
import React from 'react';
import {
Datagrid,
EmailField,
List,
TextField,
ShowButton,
EditButton,
DeleteButton,
CloneButton,
} from 'react-admin';
import { makeStyles } from '#material-ui/core/styles';
import ArchiveIcon from '#material-ui/icons/Archive';
const useRowActionToolbarStyles = makeStyles({
toolbar: {
alignItems: 'center',
float: 'right',
width: '160px',
marginTop: -1,
marginBottom: -1,
},
icon_action_button: {
minWidth: '40px;'
},
});
const ArchiveButton = props => {
const transform = data => ({
...data,
archived: true
});
return <CloneButton {...props} transform={transform} />;
}
const RowActionToolbar = (props) => {
const classes = useRowActionToolbarStyles();
return (
<div className={classes.toolbar}>
<ShowButton label="" basePath={props.basePath} record={props.record} className={classes.icon_action_button}/>
<EditButton label="" basePath={props.basePath} record={props.record} className={classes.icon_action_button}/>
<ArchiveButton {...props} basePath={props.basePath} label="" icon={<ArchiveIcon/>} record={props.record} className={classes.icon_action_button} />
<DeleteButton basePath={props.basePath} label="" record={props.record} className={classes.icon_action_button}/>
</div>
);
};
export const UserList = props => {
return (
<List
{...props}
sort={{ field: 'first_name', order: 'ASC' }}
>
<Datagrid>
<TextField source="first_name"/>
<TextField source="last_name"/>
<EmailField source="email"/>
<RowActionToolbar/>
</Datagrid>
</List>
)
};
Obviously, this code does not work because the <CloneButton> component get rid of the id the record. Moreover, except if I did something wrong - which is totally possible -, it makes a GET request to a create endpoint.
I'm using different routes in my dataProvider (The back end is using Django and Django rest framework). I want to send a PATCH to the detail endpoint, like the <Edit> component does.
I also tried with a <SaveButton>, but it fails too.
Uncaught TypeError: Cannot read property 'save' of undefined
at useSaveContext (SaveContext.js:23)
I guess the <SaveButton> must be within a <SimpleForm>?
I'd like the save behaviour of the <DeleteButton>, i.e. update the record from the list, display the notification that the record has been archived (with the Undo link), send the request to the back end, refresh the list.
Any guidance, directions would be very appreciated.
I don't know that this is a full answer, but felt like more than a comment...
You are trying to archive the existing record, not create a whole new record, right? CloneButton is supposed to be used to create a new record with a new ID (which is why your ID is going away), so you don't want to us it here. note that I've never used CloneButton. it is not fully documented so I could be wrong about its use.
I am thinking that you should use the useRecordContext hook within your Archive button to pull in all of the record's data, including the id; read this little section: https://marmelab.com/react-admin/Architecture.html#context-pull-dont-push
And I don't think transform is what you're looking for here. You will need to use one of the dataProvider hooks, i'm assuming useUpdate: https://marmelab.com/react-admin/Actions.html#useupdate
//first create component
const MyButton = (props: any) => {
const [sendEmailLoading, setSendEmailLoading] =
React.useState<boolean>(false);
const record = useRecordContext(props);
const sendEmail = (id: Identifier) => {
setSendEmailLoading(true)
dataProvider.sendEmail(
"notifications", { id: id })
.then(({ data }: any) => {
if (data && data.status == "success")
notify('Email send success', { type: 'success' });
setSendEmailLoading(false);
refresh();
});
};
return (
<ButtonMUI color='primary' size="small" onClick={() => {
sendEmail(record.id) }}>
{
!record.publish &&(
!sendEmailLoading ? (
translate('resources.notifications.buttons.send')
) : (
<CircularProgress size={25} thickness={2} />
)
)
}
</ButtonMUI>
)
}
//and second add to datagrid list
<Datagrid>
<NumberField source="id" />
<TextFieldRA source="subject" />
<DateField source="date" />
<BooleanField source="publish" />
{/* <EditButton /> */}
<ShowButton />
<MyButton />
</Datagrid>

How do I map an array of key.value to Chip? I have a list of a {k,v} map

Input json for Person - has a xrefAccounts{key,value} map
In the column "SubSys", I want to display "LIBRARY" and "SPORTS" as tags.
The Person json object has all the values required, but I can't figure out how to map it in DataGrid component.
I'm using react-admin 3.10
[{
...
"xrefAccounts": {
"LIBRARY": {
"id": "1",
"xrefSystemId": "LIBRARY"
},
"SPORTS": {
"id": "1",
"xrefSystemId": "SPORTS"
}
}
},
export const PersonList = props => (
<List filters={<PersonFilter />} {...props} >
<Datagrid rowClick="edit">
...
<ReferenceArrayField label="SubSys" reference="id" source="xrefAccounts.value">
{/* Find how to put multiple xref in one column SubSys */}
<SingleFieldList>
<ChipField source="value.xrefSystemId" />
</SingleFieldList>
</ReferenceArrayField>
<EditButton />
</Datagrid>
</List>
);
I want to display SubSys ["Library" "Sports"] similar to Tags["Sport" "Code" ]in this image
It doesn't work because you're using ArrayField on data which is not an array - it is an object!
So the best thing would be actually to fix your API response structure.
But ... if you can't do so - as I look at your attempt maybe I can suggest the following workaround via custom field component:
<XrefAccountsField source="xrefAccounts" />
inside
const XrefAccountsField = ({source, record = {}}) => {
const accountsObject = record[source];
// Convert the record to an array
const accounts = {
accountsArr: accountsObject ? Object.keys(accountsObject).map((key) => accountsObject[key]) : []
};
return (
<ArrayField source="accountsArr" record={accounts}>
<SingleFieldList>
<ChipField source="xrefSystemId" />
</SingleFieldList>
</ArrayField>
)
}

Adding custom success and error messages for the create and edit views

So in the docs it says just to add a prop on the edit component with an onSuccess / onFailure function and I've done that but navigating to the page to edit throws an error like this:
Warning: Unknown event handler property `onSuccess`. It will be ignored.
code here:
export const AffiliateEdit = (props) => {
const notify = useNotify();
const onSuccess = () => {
notify('Affiliate saved successfully');
}
return (
<Edit {...props} onSuccess={onSuccess}>
<SimpleForm redirect="list">
<ColorInput source="color" />
<TextInput source="name" validate={[required()]} />
<ReferenceInput
source="network_id"
reference="networks"
validate={[required()]}
>
<SelectInput optionText="name" />
</ReferenceInput>
<TextInput
source="reference"
validate={[required()]}
helperText="Please use all lower case, no spaces or underscores e.g affiliatename"
/>
</SimpleForm>
</Edit>
)
};
doc ref here: https://marmelab.com/react-admin/CreateEdit.html
Nvm figured it out, just needed to update react-admin lol

React-Admin <SimpleForm> component doesn't trigger the "UPDATE" action in the data provider

So I'm using the ra-data-json-server as a data provider and the bult in <SimpleForm> component as a form for the <Edit> view, and I'm facing a pretty strange issue, as it says in documentation, when submitted, the <SimpleForm> forces data provider to make a PUT request to the API, but in my case it does not.
Here's my Edit view compnent:
export const UserEdit = props => {
return (
<Edit {...props}>
<SimpleForm>
<ArrayInput source="applications">
<SimpleFormIterator source="applications">
{/* some inputs */}
</SimpleFormIterator>
</ArrayInput>
</SimpleForm>
</Edit>
);
};
And the admin component itself:
export const AdminComp = () => {
return (
<Admin
loginPage={CustomLoginPage}
authProvider={authProvider}
locale="ru"
i18nProvider={i18nProvider}
dataProvider={dataProvider}
>
<Resource
name="students"
list={UserList}
edit={UserEdit}
/>
</Admin>
);
};
And everytime I get into the Edit view and hit the save button it just doesn't do anything.
I managed to make it at least call the update in data provider, but it would call it with the old form data even though it was modified already.
I also tried reinstalling react-admin to the latest version which is what some people reccomended but it didn't help.

How to display a list in a create view in React-admin

I include a ContactList into Create Simple Form with React-Admin. The list is loaded, but when I sort, navigate or change filter, I'm redirecting to my ContactList page (/#/contacts). It's NOT a Many to many reference so I can't use component ReferenceManyField.
I've tried to change the basePath for my ContactList
<Create {...props}>
<SimpleForm>
<TextInput source="name"/>
<ContactList {...props}
basePath="/marketing/create"
className="FormInput-input-100"
resource="contacts"
match={isExact : true, params: {}, path: "/contacs/", url: "/contacts/"}
location={hash: "", pathname: "/contacts"};
hasList={true}
hasCreate={false}
hasEdit={false}
hasShow={false}
/>
</SimpleForm>
</Create>