How do I add checkboxes to admin-on-rest admin on rest create form and post the checkbox value to backend - admin-on-rest

I have added the material-ui/Checkbox component in admin-on-rest create form with source attribute. But after I click save button, I could not see the checkbox value in posted data.
But I can see 'title' and 'body' fields value in posted data. Can someone please tell, Why this code is not working?
Here is my sample code:
export const PostCreate = (props) => (
<Create {...props} >
<SimpleForm>
<TextInput source="title" />
<LongTextInput source="body" />
<Checkbox
label="Label on the left"
labelPosition="left"
source="test"
value="yes"
/>
</SimpleForm></Create>
);

Sure Checkbox is not a react-on-admin component. Please use BooleanInput

The BooleanInput.js module from rect-admin, Switch is replaced with Checkbox:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import FormControlLabel from '#material-ui/core/FormControlLabel'
import FormGroup from '#material-ui/core/FormGroup'
import Checkbox from '#material-ui/core/Checkbox'
import { addField, FieldTitle } from 'ra-core'
const sanitizeRestProps = ({
alwaysOn,
basePath,
component,
defaultValue,
formClassName,
initializeForm,
input,
isRequired,
label,
locale,
meta,
options,
optionText,
optionValue,
record,
resource,
allowEmpty,
source,
textAlign,
translate,
translateChoice,
...rest
}) => rest
export class CheckboxInput extends Component {
handleChange = (event, value) => {
this.props.input.onChange(value)
}
render() {
const {
className,
input,
isRequired,
label,
source,
resource,
options,
...rest
} = this.props
return (
<FormGroup className={className} {...sanitizeRestProps(rest)}>
<FormControlLabel
control={
<Checkbox
color="primary"
checked={!!input.value}
onChange={this.handleChange}
{...options}
/>
}
label={
<FieldTitle
label={label}
source={source}
resource={resource}
isRequired={isRequired}
/>
}
/>
</FormGroup>
)
}
}
CheckboxInput.propTypes = {
className: PropTypes.string,
input: PropTypes.object,
isRequired: PropTypes.bool,
label: PropTypes.string,
resource: PropTypes.string,
source: PropTypes.string,
options: PropTypes.object,
}
CheckboxInput.defaultProps = {
options: {},
}
export default addField(CheckboxInput)

Related

React Redux Material-UI autocomplete

I am struggling to get the value out of the Material-ui Autocomplete when using redux-form. Has anyone solved this? I am using the exact example from the material-ui Autocomplete https://material-ui.com/components/autocomplete/ I am able to see the list options and it populates after clicking it twice, but I am unable to extract the real value, instead I am returning ({ title : 0 }) instead of the value.
import React from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { Field, reduxForm } from "redux-form";
import { connect } from "react-redux";
class Form extends React.Component {
onSubmit = formValues => {
console.log(formValues);
};
renderTextField = ({
label,
input,
meta: { touched, invalid, error },
...custom
}) => (
<Autocomplete
label={label}
options={this.props.movies}
placeholder={label}
getOptionLabel={option => option.title}
onChange={input.onChange}
{...input}
{...custom}
renderInput={params => (
<TextField {...params} label={label} variant="outlined" fullWidth />
)}
/>
);
render() {
const { handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.onSubmit)}>
<Field
name="propertySelector"
component={this.renderTextField}
label="Select Property"
type="text"
/>
</form>
</div>
);
}
}
const mapStateToProps = state => {
console.log(state);
return {
movies: [
{ title: "The Shawshank Redemption", year: 1994 },
{ title: "The Godfather", year: 1972 },
{ title: "Schindler's List", year: 1993 }
]
};
};
Form = reduxForm({
form: "auto_complete"
});
export default connect(mapStateToProps, null)(Form);
Solved by passing in the (event, value) to the onChange props.
onChange={(event, value) => console.log(value)}
From the docs;
Callback fired when the value changes.
Signature:
function(event: object, value: T) => void
event: The event source of the callback.
value: null

Redux-Form will not submit if more than one Field

Why does my redux-form not submit when I have more than one field?
If I have more than one field then the onSubmit on my form does not run.
The following code will not show the alert :
//#flow
import * as React from 'react';
import {Field, reduxForm, Form} from 'redux-form';
class CustomerPage2 extends React.Component {
constructor(props) {
super(props);
}
render() {
let submit = () => alert("show me the money")
return (
<Form id="myform" onSubmit={submit} >
<Field
label={'asdf'}
className={'input'}
id='1'
name={'salutation'}
mandatory={true}
component='input'
/>
<Field
label={'asdf2'}
className={'input'}
id='2'
name={'first_name'}
mandatory={true}
component='input'
/>
</Form>
);
}
}
export default reduxForm({
form: 'customerRegistration',
})(CustomerPage2)
However if I remove one of the fields the alert will pop up :
render() {
let submit = () => alert("show me the money")
return (
<Form id="myform" onSubmit={submit} >
<Field
label={'asdf'}
className={'input'}
id='1'
name={'salutation'}
mandatory={true}
component='input'
/>
</Form>
);
}
I also created a fiddle where you can see it for your own eyes :
https://jsfiddle.net/036ur33k/150/
Just remove one of the fields and you will see what I mean.
I think you forgot to use the handleSubmit function (redux-form adds it on the component props) in your onSubmit event.
I modified your fiddle, check if it is what you need.
https://jsfiddle.net/036ur33k/173/

Jow to enable the circular progress when the user clicks on submit in login page? [admin-on-rest]

How to enable the circular progress when user clicks on submit on the login page? I can able to see the loader symbol in the app bar on other pages but I'm not able to activate it on the login page.
We need to add Custom reducer for login page. I did it in the following way.
1.1. Create a new login page. Just copy and paste the admin-on-rest login page code.
1.2. Update the propTypes like below
Login.propTypes = {
...propTypes,
authClient: PropTypes.func,
previousRoute: PropTypes.string,
theme: PropTypes.object.isRequired,
translate: PropTypes.func.isRequired,
userLogin: PropTypes.func.isRequired,
isLogging: PropTypes.bool.isRequired,
};
1.3. Add the below line
function mapStateToProps(state, props) {
return {
isLogging: state.loginReducer > 0
};
}
1.4. Update the login page with below code.
const enhance = compose(
translate,
reduxForm({
form: 'signIn',
validate: (values, props) => {
const errors = {};
const { translate } = props;
if (!values.username) errors.username = translate('aor.validation.required');
if (!values.password) errors.password = translate('aor.validation.required');
return errors;
},
}),
connect(mapStateToProps, { userLogin: userLoginAction }),
);
export default enhance(Login);
1.5. Replace the submit button code
<CardActions>
<RaisedButton type="submit" primary disabled={isLogging} icon={isLogging && <CircularProgress size={25} thickness={2} />} label={translate('aor.auth.sign_in')} fullWidth />
</CardActions>
1.6 The complete code for the login page is
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { propTypes, reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import { Card, CardActions } from 'material-ui/Card';
import Avatar from 'material-ui/Avatar';
import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';
import CircularProgress from 'material-ui/CircularProgress';
import { cyan500, pinkA200, white } from 'material-ui/styles/colors';
import defaultTheme, {translate, Notification, userLogin as userLoginAction } from 'admin-on-rest';
const styles = {
main: {
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
alignItems: 'center',
justifyContent: 'center',
},
card: {
minWidth: 300,
},
avatar: {
margin: '1em',
textAlign: 'center ',
},
avatarText:{
verticalAlign:'middle',
fontSize:20,
},
form: {
padding: '0 1em 1em 1em',
},
input: {
display: 'flex',
},
};
function getColorsFromTheme(theme) {
if (!theme) return { primary1Color: cyan500, accent1Color: pinkA200 };
const {
palette: {
primary1Color,
accent1Color,
},
} = theme;
return { primary1Color, accent1Color };
}
// see http://redux-form.com/6.4.3/examples/material-ui/
const renderInput = ({ meta: { touched, error } = {}, input: { ...inputProps }, ...props }) =>
<TextField
errorText={touched && error}
{...inputProps}
{...props}
fullWidth
/>;
class Login extends Component {
login = (auth) => this.props.userLogin(auth, this.props.location.state ? this.props.location.state.nextPathname : '/');
render() {
const { handleSubmit, submitting, theme, translate, isLogging } = this.props;
const muiTheme = getMuiTheme(theme);
const { primary1Color } = getColorsFromTheme(muiTheme);
return (
<MuiThemeProvider muiTheme={muiTheme}>
<div style={{ ...styles.main, backgroundColor: primary1Color }}>
<Card style={styles.card}>
<div style={styles.avatar}>
<div>
<Avatar backgroundColor={white} src="EnsembleGreenLogo.png" size={45} />
</div>
<div>
<span style={styles.avatarText}>Ensemble SmartWAN Manager</span>
</div>
</div>
<form onSubmit={handleSubmit(this.login)}>
<div style={styles.form}>
<div style={styles.input} >
<Field
name="username"
component={renderInput}
floatingLabelText={translate('aor.auth.username')}
disabled={submitting}
/>
</div>
<div style={styles.input}>
<Field
name="password"
component={renderInput}
floatingLabelText={translate('aor.auth.password')}
type="password"
disabled={submitting}
/>
</div>
</div>
<CardActions>
<RaisedButton
type="submit"
primary
disabled={isLogging}
icon={isLogging && <CircularProgress size={25} thickness={2} />}
label={translate('aor.auth.sign_in')}
fullWidth
/>
</CardActions>
</form>
</Card>
<Notification />
</div>
</MuiThemeProvider>
);
}
}
Login.propTypes = {
...propTypes,
authClient: PropTypes.func,
previousRoute: PropTypes.string,
theme: PropTypes.object.isRequired,
translate: PropTypes.func.isRequired,
userLogin: PropTypes.func.isRequired,
isLogging: PropTypes.bool.isRequired,
};
Login.defaultProps = {
theme: defaultTheme,
};
function mapStateToProps(state, props) {
return {
isLogging: state.loginReducer > 0
};
}
const enhance = compose(
translate,
reduxForm({
form: 'signIn',
validate: (values, props) => {
const errors = {};
const { translate } = props;
if (!values.username) errors.username = translate('aor.validation.required');
if (!values.password) errors.password = translate('aor.validation.required');
return errors;
},
}),
connect(mapStateToProps, { userLogin: userLoginAction }),
);
export default enhance(Login);
2.1. Add a new file (src/loginReducer.js) in src folder with the below content
import { USER_LOGIN_LOADING, USER_LOGIN_SUCCESS, USER_LOGIN_FAILURE, USER_CHECK } from 'admin-on-rest';
export default (previousState = 0, { type }) => {
switch (type) {
case USER_LOGIN_LOADING:
return previousState + 1;
case USER_LOGIN_SUCCESS:
case USER_LOGIN_FAILURE:
case USER_CHECK:
return Math.max(previousState - 1, 0);
default:
return previousState;
}
};
3.1 Update the app.js admin tag.
<Admin
menu={createMenus}
loginPage={Login}
dashboard={Dashboard}
appLayout={Layout}
customReducers={{ loginReducer }}
>
3.2 import the login page and login reducers in app.js
import loginReducer from './loginReducer';
import Login from "./Login";

Admin on rest custom button

I would like to make a custom button that would be used to fetch. I want the button to be usable like this:
export const LogList = (props) => (
<List {...props} perPage={100} title="Logs and Reports" filters={< FileFilter/>}>
<Datagrid>
<TextField source="inputfile" label="Input File" />
<TextField source="cycle" label="Cycle" />
<TextField source="job" label="Job" />
<TextField source="name" label="File Name" />
<ShowButton/>
<JobCancel/>
</Datagrid>
</List>
);
Where is my button is <JobCancel/> up above (similar to how ShowButton is implemented). I want the button to fetch(controller_service/archivedfiles/${id}, { method: 'DELETE', body:{} }); on click.
Is something like this possible?
P.S. I am new to Admin on rest
You can also find an example for custom actions in the demo repository for reviews (accept, reject): https://github.com/marmelab/admin-on-rest-demo/tree/master/src/reviews
Misread your question. So am editing my answer.
I have custom button for my list view.
It's a straightforward Redux connected component.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import RaisedButton from 'material-ui/RaisedButton';
import { editorAssign as editorAssignAction} from '../customActions/EditorAssignActions'
import styles from '../styles/styles'
class EditorAssignButton extends Component {
constructor(props){
super(props);
this.state = { disabled: false };
}
handleClick = () => {
const { editorAssign, record } = this.props
editorAssign(record.id) //call the action
this.setState({
disabled: true
})
}
render() {
const editorAssignStyle = styles.editorAssignStyle;
return (<RaisedButton label='Add To Edit'
onClick={this.handleClick}
disabled={ this.state.disabled }
primary={true}
/>)
}
}
EditorAssignButton.propTypes = {
editorAssign: PropTypes.func,
record: PropTypes.object
}
export default connect(null, {
editorAssign: editorAssignAction
})(EditorAssignButton)
AOR has documentation on how to write custom actions and trigger side effects with Sagas.
https://marmelab.com/admin-on-rest/Actions.html
DELETE is an action available with AOR Rest so your requirement should be quite standard.
Here is the EditorAssign view. It is a straightforward list and datagrid component
import React from 'react';
import { ReferenceField,
ChipField,
SelectInput,
ReferenceInput,
TextField,
List,
Filter,
Datagrid} from 'admin-on-rest';
import AssignTaleEditToSelf from '../buttons/AssignTaleEditToSelf'
const EditorAssignView = (props) => {
return (
<List {...props} title="Fresh Tales" perPage={20} sort={{ field: 'id', order: 'ASC' }} filter={{"status": "NEW"}} filters={ <EditorFilter /> } >
<Datagrid >
<TextField source="id" label="id" style={{ textAlign: 'center'}} />
<TextField source="taleTitle" label="Title" />
<TextField source="taleText" label="Content" style={{maxWidth: '150px'}} />
<ReferenceField label="Writer" source="writer_id" reference="appUsers">
<ChipField source="name" />
</ReferenceField>
<AssignTaleEditToSelf label="Assign To Self" />
</CustomDatagrid>
</List>
)
}
}

"Cannot read property 'requesting' of undefined" when passed custom props to component

I got an issue may cause from react-boilerplate Or "redux-form/immutable", wish someone can help me out.
I tried to put some custom props into Form component and this will print out messages of error when submit.
Here is my code:
import React from 'react';
import { Form, Icon } from 'semantic-ui-react';
import { PropTypes } from 'prop-types';
import { Field, reduxForm, reset } from 'redux-form/immutable';
import { connect } from 'react-redux';
import { ReduxFormInput, ReduxFormCheckbox } from '../../components/ReduxFormInput';
import { signupSync, passStrength } from '../../components/Validate';
import StyledButton from '../../components/StyledButton';
import AcceptTerms from './acceptTerms';
import signupRequest from './actions';
class Signup extends React.Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
signupRequest: PropTypes.func,
signup: PropTypes.shape({
requesting: PropTypes.bool,
successful: PropTypes.bool,
messages: PropTypes.array,
errors: PropTypes.array,
}),
}
submit(values, dispatch) {
console.log(values);
this.props.signupRequest(values); // will be undefined 'props' after submit
}
render() {
const {
handleSubmit,
submitting,
signup: {
requesting,
successful,
messages,
errors,
},
} = this.props;
return (
<Form onSubmit={handleSubmit(this.submit)} >
<Form.Field>
<Field
type="text"
name="accountName"
component={ReduxFormInput}
icon="user outline"
label="Tên tài khoản"
placeholder="Tên tài khoản của bạn"
/>
</Form.Field>
<Form.Field >
<Field
type="email"
name="email"
component={ReduxFormInput}
icon="mail outline"
label="Email"
placeholder="Email của bạn"
/>
</Form.Field>
<Form.Field required >
<Field
type="password"
name="password"
component={ReduxFormInput}
icon="lock"
label="Mật khẩu"
placeholder="Nhập mật khẩu"
warn={passStrength}
/>
</Form.Field>
<Form.Field required >
<Field
type="password"
name="confirmPassword"
component={ReduxFormInput}
icon="lock"
label="Xác nhận Mật khẩu"
placeholder="Xác nhận lại mật khẩu"
/>
</Form.Field>
<Form.Field>
<Field
defaultChecked
type="checkbox"
name="confirm"
checkboxLabel="Tôi muốn nhận thông tin thông qua email, SMS, hoặc điện thoại."
component={ReduxFormCheckbox}
/>
</Form.Field>
{AcceptTerms}
<div>
<StyledButton primary fluid type="submit" disabled={submitting} >
<Icon name="add user" />
Đăng ký tài khoản
</StyledButton>
</div>
</Form>
);
}
}
const mapStateToProps = (state) => ({
signup: state.signup,
});
const connected = connect(mapStateToProps, { signupRequest })(Signup);
const formed = reduxForm({
form: 'signup',
validate: signupSync,
onSubmitSuccess: afterSubmit,
})(connected);
const afterSubmit = (result, dispatch) => dispatch(reset('signup'));
export default formed;
My reducer
import { SubmissionError } from 'redux-form/immutable';
import {
SIGNUP_REQUESTING,
SIGNUP_SUCCESS,
SIGNUP_ERROR,
} from './constants';
const initialState = {
requesting: false,
successful: false,
errors: [],
messages: [],
};
const reducer = function signupReducer(state = initialState, action) {
switch (action.type) {
case SIGNUP_REQUESTING:
return {
requesting: true,
successful: false,
errors: [],
messages: [{
body: 'Signing up...',
time: new Date(),
}],
};
case SIGNUP_SUCCESS:
return {
requesting: false,
successful: true,
errors: [],
messages: [{
body: `Successfully created account for ${action.response.email}`,
time: new Date(),
}],
};
case SIGNUP_ERROR:
return {
requesting: false,
successful: false,
messages: [],
errors: new SubmissionError({
email: 'failed',
_error: 'failed',
}),
};
default:
return state;
}
};
export default reducer;
Inject reducer on routes.js
...
{
path: '/signup',
name: 'signup',
getComponent(nextState, cb) {
const importModules = Promise.all([
import('containers/SignupPage/reducer'),
import('containers/SignupPage/sagas'),
import('containers/SignupPage'),
]);
const renderRoute = loadModule(cb);
importModules.then(([reducer, sagas, component]) => {
injectReducer('signup', reducer.default);
injectSagas(sagas.default);
renderRoute(component);
});
},
}
...
Then I got an error screen like this.
TypeError: Cannot read property 'requesting' of undefined
Signup.render
/Users/son/Desktop/we-mak/app/containers/SignupPage/signupForm.js
Signup.tryRender
http://localhost:3000/main.js:1388:1
Signup.proxiedMethod
http://localhost:3000/main.js:1356:1
eval
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:796:21
measureLifeCyclePerf
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:75:12
ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:795:25
ReactCompositeComponentWrapper._renderValidatedComponent
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:822:32
ReactCompositeComponentWrapper._updateRenderedComponent
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:746:36
ReactCompositeComponentWrapper._performComponentUpdate
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:724:10
ReactCompositeComponentWrapper.updateComponent
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:645:12
I notice that react-boilerplate doesn't use react-hot-loader so I guessed it may cause from boilerplate, but I don't have enough webpack experience to config it.
This error message means your signup property is undefined, which may happen if your state does not have signup property or that property is undefined. Have a look at your reducer.

Resources