How to create a ReferenceInput wrapped component? - admin-on-rest

I'm trying to create a wrap component that includes admin-on-rest ReferenceInput
What am I missing?
I have seen the answer Error on Creating custom ReferenceInput with separate component but I do not know how to apply it in this case
//Works!!
export const commentsCreate = (props) => (
<Create title = "Create" {...props} >
<SimpleForm>
<ReferenceInput label="Posts" source="field" reference="posts" allowEmpty {...props}>
<AutocompleteInput optionText="name" {...props}/>
</ReferenceInput>
</SimpleForm>
</Create>
);
/*
Fail:
ReferenceInput.js:303 Uncaught (in promise) TypeError: Cannot read property 'value' of undefined
at Function.mapStateToProps [as mapToProps] (ReferenceInput.js:303)
at mapToPropsProxy (wrapMapToProps.js:43)
.......
*/
export const commentsCreate = (props) => (
<Create title = "Create" {...props} >
<SimpleForm>
<Prueba {...props} />
</SimpleForm>
</Create>
);
const Prueba = (props) => (
<ReferenceInput label="Posts" source="field" reference="posts" allowEmpty {...props}>
<AutocompleteInput optionText="name" {...props}/>
</ReferenceInput>
);

I found a (the?) solution.
We need to use a redux-form Field component in Simpleform.
import { Field } from 'redux-form';
export const commentsCreate = (props) => (
<Create title = "Create" {...props} >
<SimpleForm>
<Field name="field" component={Prueba} {...props} />
</SimpleForm>
</Create>
);
const Prueba = (props) => (
<ReferenceInput label="Posts" source="field" reference="posts" allowEmpty {...props}>
<AutocompleteInput optionText="name" {...props}/>
</ReferenceInput>
);
From https://redux-form.com/6.0.0-alpha.4/docs/api/field.md/
The Field component is how you connect each individual input to the Redux store. There are three fundamental things that you need to understand in order to use Field correctly:
The name prop is required. It is a string path, in dot-and-bracket notation, corresponding to a value in the form values. It may be as simple as 'firstName' or as complicated as contact.billing.address[2].phones[1].areaCode.
The component prop is required. It may be a Component, a stateless function, or a factory, like those provided under React.DOM. See Usage section below. To learn about the props that will provided to whatever your component is, see the Props section below.
All other props will be passed along to the element generated by the component prop.

is question actual now? Some time ago I divided component this way:
1) create file DescriptionField.js
2) write code into it
import React from 'react';
export const DescriptionField = ({ source = "Description" }) => <span><p/><p/>{source}<p/></span>;
export default DescriptionField;
(maybe it can be simple). Maybe you forgot export ?
3) and in parent component call it:
export const SomeMyCreate = (props) => (
<Create {...props}>
<SimpleForm>
.....
<DescriptionField source="Warn! Only one picture may be here!"/>
</SimpleForm>
</Create>
);
You can try to do it the same method. Please write your code Prueba component file here

Related

React-bootstrap (BS v3) - can not use a ref from an input

I’m using react-bootstrap, but with bootstrap v3, because that’s the bootstrap version my project currently uses.
Now, I just need to have a ref from an input. In the react-bootstrap docs, it says you should use formControl’s inputRef property like this:
<FormControl inputRef={ref => { this.input = ref; }} />
But currently, I’m using a function and react hooks to build my component, instead of classes.
So I just wrote my code this way:
let inputReferencia = useRef(null);
In onFocus event, I use this statement to select the content of the input:
inputReferencia.current.select();
And, finally, this is my input, as per react-bootstrap syntax:
<FormGroup>
<FieldGroup
id="referencia"
name="referencia"
value={formValues.referencia}
type="text"
label="Referencia"
onFocus={(e) => onDescripcionReferenciaInputFocus(e)}
onChange={(e) => onInputChange(e)}
inputRef={ref => { inputReferencia = ref; }} />
</FormGroup>
As React-bootstrap suggests, this is FieldGroup:
const FieldGroup = ({ id, label, help, ...props }) => {
return (
<FormGroup controlId={id} bsSize="small">
<ControlLabel>{label}</ControlLabel>
<FormControl {...props} />
{help && <HelpBlock>{help}</HelpBlock>}
</FormGroup>
);
}
But when I try to access the ref, as in
inputReferencia.current.select()
the current property is undefined.
Of course, if I check out the ref in Chrome debugger, it was definitely initialized with something:
Can somebody help me to solve this issue?
Many thanks for your help …
According to the docs you ought to use ref instead of inputRef. This is regardless of whether you are using Bootstrap V3 or Bootstrap V4 or any other version as React-bootstrap only uses their stylesheet.
In dealing with functional components and trying to access the child element's ref on the parent when the ref variable is declared on the parent, you can opt to use forwardRef
function App() {
let inputReferencia = useRef(null);
function onDescripcionReferenciaInputFocus(e) {
console.log(`inputReferencia`, inputReferencia);
}
return (
<Container>
<FormGroup>
<FieldGroup
id="referencia"
name="referencia"
value={formValues.referencia}
type="text"
label="Referencia"
onFocus={(e) => onDescripcionReferenciaInputFocus(e)}
onChange={(e) => onInputChange(e)}
ref={inputReferencia}
/>
</FormGroup>
</Container>
);
}
const FieldGroup = React.forwardRef(({ id, label, help, ...props }, ref) => {
return (
<FormGroup controlId={id} bsSize="small">
<ControlLabel>{label}</ControlLabel>
<FormControl {...props} ref={ref} />
{help && <HelpBlock>{help}</HelpBlock>}
</FormGroup>
);
});

React-Admin How to change the "Dashboard" name in the menu

How can I change the "dashoboard" menu name in the react-admin?
By default it's name is always "Dashboard" even in the Demo
the name is Dashboard. Someone knows a way to change the name or if it is even possible to change?
At the Menu.tsx, don't call it like
<DashboardMenuItem onClick={onMenuClick} sidebarIsOpen={open} />
Instead call it like a normal menu item but pointing to the dashboard component
<MenuItemLink
to={`/`} // by default react-admin renders Dashboard on this route
primaryText="Your Text"
leftIcon={<YourIcon />}
onClick={onMenuClick}
sidebarIsOpen={open}
dense={dense}
/>
Like in that answer is showed that in the MenuItemLink has an atribute called primaryText="Your Text" that you can use in your own
<DashboardMenuItem onClick={onMenuClick} classes={classes} primaryText="Your Text" />
To change the name of the default "Dashboard" name for what you want.
So more detailed answer is:
Override layout
// in src/Admin.js
<Admin
layout={MyLayout}
title="SuperCoolProject"
theme={myTheme}
dataProvider={dataProvider}
authProvider={authProvider}
>
Use custom menu in layout. (it's not sidebar)
// in src/MyLayout.js
import React from "react";
import { Layout } from "react-admin";
import MyMenu from "./MyMenu";
const MyLayout = (props) => <Layout {...props} menu={MyMenu} />;
export default MyLayout;
Change menu so it still renders resources and add custom MenuItemLinks you need (vs. one hardcoded "dashboard")
// in src/Menu.js
import React from "react";
import { useSelector } from "react-redux";
import { useMediaQuery } from "#material-ui/core";
import { MenuItemLink, getResources } from "react-admin";
import DefaultIcon from "#material-ui/icons/ViewList";
import SettingsIcon from "#material-ui/icons/Settings";
import HomeIcon from "#material-ui/icons/Home";
import HelpIcon from "#material-ui/icons/Help";
const Menu = ({ onMenuClick, logout }) => {
const isXSmall = useMediaQuery((theme) => theme.breakpoints.down("xs"));
const open = useSelector((state) => state.admin.ui.sidebarOpen);
const resources = useSelector(getResources);
return (
<div>
<MenuItemLink
to="/home"
primaryText="Home"
leftIcon={<HomeIcon />}
onClick={onMenuClick}
sidebarIsOpen={open}
/>
{resources.map((resource) => (
<MenuItemLink
key={resource.name}
to={`/${resource.name}`}
primaryText={
(resource.options && resource.options.label) || resource.name
}
leftIcon={resource.icon ? <resource.icon /> : <DefaultIcon />}
onClick={onMenuClick}
sidebarIsOpen={open}
/>
))}
<MenuItemLink
to="/custom-route"
primaryText="Settings"
leftIcon={<SettingsIcon />}
onClick={onMenuClick}
sidebarIsOpen={open}
/>
<MenuItemLink
to="/help-center"
primaryText="Help Center"
leftIcon={<HelpIcon />}
onClick={onMenuClick}
sidebarIsOpen={open}
/>
{isXSmall && logout}
</div>
);
};
export default Menu;

using filters cause error in admin-on-resst

this is my code
import React from 'react';
import {
List,
Datagrid,
TextField,
DateField,
Filter,
TextInput,
} from 'admin-on-rest/lib/mui';
const fsFilter = props => (
<Filter {...props}>
<TextInput label="Search" source="q" alwaysOn />
</Filter>
);
export const fsList = props => (
<List
{...props}
filters={<fsFilter />}
>
...
I want to use filters in my list (search input) but I'm getting 2 warnings
Warning: Unknown props `showFilter`, `displayedFilters`, `filterValues`, `context` on <fsFilter> tag. Remove these props from the element...
and
Warning: Unknown props `hideFilter`, `setFilters` on <fsFilter> tag. Remove these props from the element...
what's wrong?

How to use Redux-Form with React-Bootstrap?

I am trying to use "redux-form": "^6.7.0" with "react-bootstrap": "^0.31.0"
My Component renders nicely, but when I press Submit, what I see is an empty object.
note: I have tried wrapping the Config with connect first, and as show below, first wraping it with redux-form and then with the from react-redux connect()
Configuration.js
class Config extends Component {
render() {
const { ServerPort, UserID, PortNumber, WWWUrl, SourcePath, FMEPath, pickFile, pickFolder, handleSubmit } = this.props;
return (
<Form horizontal onSubmit={handleSubmit}>
<FormGroup controlId="serverPortBox">
<Col componentClass={ControlLabel} sm={2}>Watson Port:</Col>
<Col sm={10}>
<OverlayTrigger placement="left" overlay={(<Tooltip id="tt1">TCP port for Watson to use</Tooltip>)}>
<Field name="WatsonPort" component={FormControl}
type="number" min="1024" max="65535" placeholder={ServerPort} />
</OverlayTrigger>
</Col>
</FormGroup>
......
const CForm = reduxForm({
form: 'configuration' // a unique name for this form
})(Config);
const Configuration = connect(mapStateToProps, mapDispatchToProps)(CForm)
export default Configuration
reducers.js
import { combineReducers } from 'redux'
import { reducer as formReducer } from 'redux-form
......
const reducerList = {
GLVersion,
ServerConfig,
ServerStats,
form: formReducer
}
export default combineReducers(reducerList)
Main Package Dashboard.js
what i see in the debugger is that config is an empty object
<Panel className='configPanel'
collapsible header="Configuration"
eventKey="1"
defaultExpanded={true}>
<Configuration onSubmit={(config) => writeConfig(config)} />
</Panel>
See: https://github.com/erikras/redux-form/issues/2917
Oh, this was a great mystery. I followed the advice in https://github.com/react-bootstrap/react-bootstrap/issues/2210 and both the warning about additional props and the empty submit stopped.
It seems you have to wrap the Bootstrap in your custom component (why?, I don't know). Also make sure you custom component is a stateless funcitonal component, or after the first key press, you field will blur and lose focus.
There are some warnings in the documentation of redux-form about this.
my custom field component FieldInput
const FieldInput = ({ input, meta, type, placeholder, min, max }) => {
return (
<FormControl
type={type}
placeholder={placeholder}
min={min}
max={max}
value={input.value}
onChange={input.onChange} />
)
}
and I invoke it like this:
<Field name="ServerPort"
type='number'
component={FieldInput}
placeholder={ServerPort}
min="1024" max="65535"
/>
see also: https://github.com/erikras/redux-form/issues/1750
So now, the definition of FieldInput and Config look like this:
import React, { Component } from 'react'
import { Field, reduxForm } from 'redux-form'
import { connect } from 'react-redux'
import { Form, FormControl, FormGroup, ControlLabel, Col, Button, Tooltip, OverlayTrigger } from 'react-bootstrap'
import * as Act from '../dash/actions.js'
import FaFolderOpen from 'react-icons/lib/fa/folder-open'
import FaFileCodeO from 'react-icons/lib/fa/file-code-o'
const FieldInput = ({ input, meta, type, placeholder, min, max }) => {
return (
<FormControl
type={type}
placeholder={placeholder}
min={min}
max={max}
value={input.value}
onChange={input.onChange} />
)
}
const Config = ({ ServerPort, UserID, PortNumber, WWWUrl, SourcePath, FMEPath, pickFile, pickFolder, handleSubmit }) => {
return (
<Form horizontal onSubmit={handleSubmit}>
<FormGroup controlId="serverPortBox">
<Col componentClass={ControlLabel} sm={2}>Watson Port:</Col>
<Col sm={10}>
<OverlayTrigger placement="left" overlay={(<Tooltip id="tt1">TCP port for Watson to use</Tooltip>)}>
<Field name="ServerPort" type='number' min="1024" max="65535" component={FieldInput} placeholder={ServerPort} />
</OverlayTrigger>
</Col>
</FormGroup>
Some props required by <FormControl> are passed inside props.input from <Field>, see http://redux-form.com/6.6.3/docs/api/Field.md/#props
To pass all those props in a generic way, instead of doing it explicitly, you can use the following function:
const ReduxFormControl = ({input, meta, ...props}) => {
return <FormControl {...props} {...input} />
};
and then inside the form:
<Field component={ReduxFormControl} ... />
This way, value, onChange, etc. are all passed as expected to <FormControl>.

In Redux Forms, how do I validate one field based on the value of another?

I am using Redux Form version 6.4.3 and I'm trying to validate two date fields such that the 'to' date must always be before the 'from' date.
Other examples say I ought to be able to refer to the fields array in props but there is no such array. The form state has an array called registeredFields however but those just seem to be of the form {name: 'dob', type: 'Field'}
Here is my form code
import React from 'react'
import DatePicker from 'react-bootstrap-date-picker'
import moment from 'moment'
import {Field, reduxForm} from 'redux-form'
import {Form, Row, Col, Button, FormGroup, ControlLabel, FormControl, HelpBlock} from 'react-bootstrap'
// validations
const required = value => !value ? 'This field is required' : undefined
const maxDate = max => value =>
value && moment(value).isAfter(max) ? `Must be before ${max}` : undefined
const minDate = min => value =>
value && moment(value).isBefore(min) ? `Must be after ${min}` : undefined
const renderDatepicker = ({ input, label, hint, showTodayButton, meta: { pristine, touched, warning, error } }) => {
const validationState = pristine ? null : error ? 'error' : warning ? 'warning' : null
return (
<FormGroup validationState={validationState}>
<Col componentClass={ControlLabel} sm={3}>{label}</Col>
<Col sm={3}>
<FormControl
{...input}
componentClass={DatePicker}
placeholder="DD/MM/YYYY"
dateFormat="DD/MM/YYYY"
showTodayButton={showTodayButton}/>
</Col>
{pristine && !!hint && (
<Col sm={6}>
<HelpBlock>{hint}</HelpBlock>
</Col>
)}
{touched && (
(error && (
<Col sm={6}>
<HelpBlock>{error}</HelpBlock>
</Col>)
) || (warning && (
<Col sm={6}>
<HelpBlock>{warning}</HelpBlock>
</Col>
))
)}
</FormGroup>
)}
const MyForm = props => {
const { error, handleSubmit, pristine, reset, submitting, fields } = props
console.debug('fields', fields) // fields is undefined
return (
<Form horizontal>
<Field
name="dateFrom"
component={renderDatepicker}
label="Date from"
hint="Earliest date for enquiry"
validate={[required, maxDate('where do I get the other date value from?')]}
/>
<Field
name="dateTo"
component={renderDatepicker}
label="Date to"
showTodayButton={true}
hint="Latest date for enquiry"
validate={[required, minDate('where do I get the other date value from?')]}
/>
</Form>
)
}
export default reduxForm({
form: 'MyForm',
})(MyForm)
I get the feeling I am missing something obvious since all the examples I have seen expect that the fields array to exist in the props.
It's also worth mentioning that the Field's validate function signature is validate : (value, allValues, props) => error [optional] so you actually have a reference to other fields values in field-level validation as well.
I believe you could do something like:
const maxDateValidationAdapter = (value, values) => maxDate(values.dateTo)(value);
// Alternatively, if maxDate() is used only in this form you can just change its signature
<Field
name="dateFrom"
component={renderDatepicker}
label="Date from"
hint="Earliest date for enquiry"
validate={[required, maxDateValidationAdapter]}
/>
See Field documentation (Props you can pass to Field => validate).
For Redux Form, use:
export default reduxForm({
form: 'MyForm', // a unique identifier for this form
validate, // <--- validation function given to redux-form
})(MyForm)
const validate = values => {
const errors = {}
// Here you can get all the fields in value object, use value.min or value.max
return errors
}
http://redux-form.com/6.4.3/examples/syncValidation/

Resources