React-admin SimpleForm does not call PUT request when click save - reactjs

In react-admin, SimpleForm should automatically call PUT request, however it does not work. Instead, for some odd reason, it's calling GET request whenever I click the save button. I'm new to react-admin and I've been looking around for solutions but I have no idea what's wrong. I have tried calling PUT request with postman and it works, so I know for sure that my backend is not the problem.
import React from 'react';
import { Provider } from 'react-redux';
import { createHashHistory } from 'history';
import simpleRestProvider from 'ra-data-simple-rest';
import createAdminStore from '../createAdminStore';
import { Admin, Resource } from 'react-admin';
import {
CategoryList,
CategoryShow,
ItemList,
ItemShow,
ItemEdit
} from '../adminComponents/posts';
const dataProvider = simpleRestProvider('http://localhost:3000/api');
const history = createHashHistory();
const AdminContainer = () => (
<Provider store={createAdminStore({dataProvider, history })}>
<Admin dataProvider={dataProvider} history={history} title="My Admin">
<Resource name="categories" list={CategoryList} show={CategoryShow} />
<Resource name="items" list={ItemList} show={ItemShow} edit={ItemEdit}/>
</Admin>
</Provider>
);
Here's Item Edit component:
export const ItemEdit = props => (
<Edit {...props} >
<SimpleForm>
<TextInput disabled source="id" />
<TextInput source="name" />
<TextInput source="description" />
<NumberInput source="price" />
{/* <TextInput source="selections" /> */}
<ReferenceInput source="category_id" reference="categories">
<SelectInput optionText="name" />
</ReferenceInput>
</SimpleForm>
</Edit>
);

It might not be related, but I just resolved this issue using react-admin 3.7. I've got an Edit component wrapping a SimpleForm, and when I removed the onSuccess attribute, the form started working again.

Related

Reset filter values when switching between lists

I have a react-admin App with multiple list resources and would want to only clear the filter when I change between lists (switching between companies/rules/lookups). Currently I am using the solution shown in RuleList.js for each list (RuleFilter part). This however also resets the filter every time you show/edit an element in the list. I don't know if there is a way to detect when you click between companies/rules/lookups that you could use to clear the filter.
App.js
import * as React from "react"
import { Admin, Resource } from 'react-admin'
import dataProvider from './dataProvider'
import companies from './companies'
import rules from './rules'
import lookups from './lookups'
const App = () => (
<Admin
dataProvider={dataProvider}>
<Resource name="companies" {...companies} />
<Resource name="rules" {...rules} />
<Resource name="lookups" {...lookups} />
</Admin>
)
export default App
RuleList.js
import React, { useState, useEffect } from 'react'
import { List, Datagrid, TextField, EditButton } from 'react-admin'
const RuleFilter = (props) => {
useEffect(() => {
const { setFilters } = props
return () => setFilters({ regex: '' })
}, [])
return (
<Filter {...props}>
<TextInput label="Search" source="regex" alwaysOn />
</Filter>
)
}
const RuleList = (props) => {
return (
<List {...props}
filters={<RuleFilter />}
exporter={false}>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="regex" />
<EditButton />
</Datagrid>
</List>
)
}
export default RuleList
index.js for Rule
import RuleList from'./RuleList'
import RuleEdit from './RuleEdit'
import RuleCreate from './RuleCreate'
const rules = {
list: RuleList,
edit: RuleEdit,
create: RuleCreate,
}
export default rules
Clear the search field when clicking companies or rules
Use redux-persist, and save your filters in Store,
OR
Use localStorage to save your filters

Component to call all material-ui Icon in React

I want to implement a component which calls the corresponding icon from material-ui. I've made it work when manually calling it.
import MenuIcon from '#material-ui/icons/Menu';
export const Icon = (props) => {
return (
<div>
<MenuIcon/>
</div>
)
}
The problem is I don't know how to change the import for all icons.
I want to call this component the following way:
<Icon icon="MenuIcon" className="someClass" />
<Icon icon="LocationOn" className="someClass" />
<Icon icon="Notifications" className="OthersomeClass" />
I can't figure out how to import all icons and how to change my Icon component to work for any icon from the material-ui package.
Something like this...
import React from 'react';
import * as IconList from '#material-ui/icons/Menu'; //error
export const Icon = (props) => {
const {icon, className} = props;
return (
<`${icon}` className={className} /> {//error}
)
}
Any ideas?
You should be able to import all the icons using named imports or the star imports (* as).
Star imports should look like this
import * as Icons from "#material-ui/icons";
<Icon icon={Icons.Menu} className="someClass" />
<Icon icon={Icons.AccessAlarmIcon} className="someClass" />
Named imports should look like this
import { Menu, AccessAlarmIcon } from "#material-ui/icons";
<Icon icon={Menu} className="someClass" />
<Icon icon={AccessAlarmIcon} className="someClass" />
You can also refactor your Icon component to utilize the React children prop, that way you can better compose each icon on the Icon component.
So it should look something like this
import React from 'react';
export const Icon = ({ children }) => {
return (
<>{children}</>
)
}
Then you can use it like this
<Icon>
<Menu className="someClass" />
</Icon>
<Icon>
<AccessAlarmIcon className="someClass" />
</Icon>
PS:
Your star imports were from '#material-ui/icons/Menu' as opposed to just '#material-ui/icons' and that caused an error

Prevent Chip component to send a REST request

I have the following code:
import React from 'react';
import PropTypes from 'prop-types';
import Chip from '#material-ui/core/Chip';
import withStyles from '#material-ui/core/styles/withStyles';
const styles = {
root: {
margin: 4,
},
};
function CustomChipField({ root, classes, record, onClick }) {
return (
<Chip className={classes.root} label={`${record.name}`} onClick={onClick} />
);
}
CustomChipField.propTypes = {
classes: PropTypes.shape({}).isRequired,
record: PropTypes.shape({}),
onClick: PropTypes.func.isRequired,
};
CustomChipField.defaultProps = {
record: {},
};
export default withStyles(styles)(CustomChipField);
What is it? It is a custom Chip component inheriting material-ui's chip.
But what I haven't figured out yet is why it sends REST request when I click it.
The example of such a request: http://localhost:3000/#/users/{"name"3A"whatever_name"}
I have an onClick prop overriden, and it was my attempt to override it but it doesn't do anything.
I use this component in the SingleFieldList of react-admin, and maybe the problem in react-admin but I use custom Chip component directly inherited from material-ui.
The code from react-admin:
export const UserList = props => (
<List {...props}>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="username" />
<ArrayField source="some_array">
<SingleFieldList>
<CustomChipField
source="name"
size="small"
clickable={true}
onClick={handleClick}
/>
</SingleFieldList>
</ArrayField>
</Datagrid>
</List>
);
And once again - onClick prop doesn't work.
So the question is: how to whether prevent Chip component sending a REST-request, whether to customize it.
This worked for me:
<SingleFieldList linkType={false}>
<CustomChipField />
</SingleFieldList>

How to share an instance between 2 List components in a react-admin Resource

This is how components are instantiated in react-admin, but now I need to share the notifications instance between the PostList and the UserList. I would like to avoid a singleton. Is it possible to pass the 'notifications' object somehow to the lists?
import React from 'react';
import PostIcon from '#material-ui/icons/Book';
import UserIcon from '#material-ui/icons/People';
import { Admin, Resource } from 'react-admin';
import jsonServerProvider from 'ra-data-json-server';
import { PostList } from './posts';
import { UserList } from './users';
import { Notifications } from './notifications';
var notifications = new Notifications();
const App = () => (
<Admin dataProvider={jsonServerProvider('https://jsonplaceholder.typicode.com')}>
<Resource name="posts" list={PostList} icon={PostIcon} />
<Resource name="users" list={UserList} icon={UserIcon} />
</Admin>
);
Thanks in advance.
It seemed to me that everything is well implemented:
https://marmelab.com/react-admin/Theming.html#notifications
import { Layout } from 'react-admin';
import MyNotification from './MyNotification';
const MyLayout = (props) => <Layout {...props} notification={MyNotification} />;
const App = () => (
<Admin layout={MyLayout} dataProvider={simpleRestProvider('http://path.to.my.api')}>
// ...
</Admin>
);
Passing custom props to the <Resource> component was not quite working for me, so on trying out several different approaches, this was the one that suited my requirements.
So, there's a default prop which you can pass to the <Resource> component called options which accepts an object. Normally, you'd see this prop being used when you want to pull a label into your resource. The idea is to customise this object -
// In App.js
<Admin dataProvider={jsonServerProvider('https://jsonplaceholder.typicode.com')}>
<Resource name="posts" list={PostList} icon={PostIcon} options={{ "label": "Posts", "notifications": notifications }} />
<Resource name="users" list={UserList} icon={UserIcon} options={{ "label": "Users", "notifications": notifications }} />
</Admin>
and then access it in your resource something like -
// In Posts.js
export const PostList = (props) => {
const { notifications, label } = props.options;
// You have your notifications object here now
return (
<List
{...props}
title={label}
bulkActionButtons={false}
exporter={false}>
<Datagrid>
<DateField showTime={true} sortable={true} source="created_on" label="Timestamp" />
</Datagrid>
</List>
);
};
Let me know if this worked for you!

Wrapper component for admin-on-rest

I would like to create a new component that contains Inputs and Fields from aor and use it in <SimpleForm> and <TabbedForm> as below:
const WrapperComp = () => {
return (
<div>
<TextFieldLabel muiTheme={muiTheme}>Title Example</TextFieldLabel>,
<TextInput source="status"/>,
<TextField source="status"/>
</div>
)
}
<SimpleForm>
<WrapperComp />
</SimpleForm>
but I get Uncaught Error: The TextInput component wasn't called within a redux-form <Field>. Did you decorate it and forget to add the addField prop to your component?.
Any help would be appreciated. Thanks!
You need to use Field from redux-form to decorate your AOR inputs and use TextField from AOR and pass {...props} as pointed by kunal pareek
import React from 'react';
import {
LongTextInput,
ImageField,
ImageInput,
TextField
} from 'admin-on-rest';
import { Field } from 'redux-form';
const CustomGroupComp = (props) => (
<div>
<TextField source="status" {...props} />
<Field name="staffComment" component={LongTextInput} label="staffComment" {...props} />
<Field name="adminComment" component={LongTextInput} label="resources.faults.fields.adminComment" {...props} />
<Field multiple name="fileA" component={ImageInput} accept="image/*">
<ImageField source="path" title="title"/>
</Field>
</div>
);
export default CustomGroupComp;
AOR SimpleForm works by passing the Redux-Form props into its children components. Since you are wrapping up your component, those props aren't passing down to the components you need. You will need to explicitly do this now. So something like
const WrapperComp = (props) => {
return (
<div>
<TextFieldLabel {...props} muiTheme={muiTheme}>Title Example</TextFieldLabel>,
<TextInput {...props} source="status"/>,
<TextField {...props} source="status"/>
</div>
)
}

Resources