4

I couldn't find way to hide input depends on some record value. I tried to get import { formValueSelector } from 'redux-form' to get current state but i failed.

export default props => 
    <Edit {...props}>
        <SimpleForm>
            <DisabledInput source="id"/>
            <NumberInput options={opts} source="age" />
            {
            props.record.age > 18 &&
                <TextInput options={opts} source="question"/>
            }
        </SimpleForm>
    </Edit>
Ismail Baskin
  • 374
  • 6
  • 9

4 Answers4

12

You can use marmelab/aor-dependent-input, it's a component for displaying input depending on other inputs values.

Example usage:

import React from 'react';
import {
    Create, SimpleForm, TextInput, DisabledInput, NumberInput
} from 'admin-on-rest';
import DependentInput from 'aor-dependent-input';

const checkAge = (age) => {
    return parseInt(age, 10) > 18;
};

const UserCreate = (props) => (
    <Create {...props}>
        <SimpleForm>
            <DisabledInput source="id"/>
            <NumberInput source="age" step="1" />

            <DependentInput dependsOn="age" resolve={checkAge}>
                <TextInput source="question"/>
            </DependentInput>
        </SimpleForm>
    </Create>
);

export default UserCreate;
Wédney Yuri
  • 1,267
  • 12
  • 21
5

The record doesn't change until you submit, so your solution doesn't work. I believe the solution is to use formValueSelector, as explained in the redux-form documentation, in a custom connected Input components.

Something like:

// in src/ConditionalInput.js
import React from 'react';
import { connect } from 'react-redux';
import { formValueSelector } from 'redux-form'
import { TextInput } from 'admin-on-rest/lib/mui`;

const ConditionalInput = ({ isDisplayed, condition, children, ...rest }) => 
    isDisplayed 
        ? React.cloneElement(children, rest)
        : null;

function mapStateToProps(state, props) {
    return {
        isDisplayed: props.condition(formValueSelector('record-form')),
    }
}

export default connect(mapStateToProps)(ConditionalInput);

// in your EditView
export default props => 
<Edit {...props}>
    <SimpleForm>
        <DisabledInput source="id"/>
        <NumberInput options={opts} source="age" />
        <ConditionalInput condition={selector => selector('age') > 18}>
            <TextInput options={opts} source="question"/>
        </ConditionalInput>
    </SimpleForm>
</Edit>
François Zaninotto
  • 7,068
  • 2
  • 35
  • 56
1

François's answer was very close and to be fair mine is not that clean, but I had a few issues with it:

  • the selector requires the state too
  • after passing in the state I had issues with Trying to access touched of undefined

After some digging I found that the touched is a meta attribute see: https://github.com/marmelab/admin-on-rest/blob/master/docs/Inputs.md . And the page states that the <Field> is passing a meta and an input attribute to its child. I tried fixing François answer but it was more change that I cared to amend his answer.

const ConditionalChildRendering = ({isDisplayed, condition, children, ...rest}) => {
  return isDisplayed
    ? React.cloneElement(children, rest)
    : null;
}

const ConditionalInput = connect((state, props) => {
  return  {
      isDisplayed: props.condition(formValueSelector('record-form'), state),
  }
})(ConditionalChildRendering);

...

let conditionalTextField = ({meta, input, name, ...rest}) => {
    return <ConditionalInput {...rest}>
        <TextInput source={name} meta={meta} input={input} {...rest}  />
    </ConditionalInput>;
};

<Field 
    component={conditionalTextField} 
    name="postcode" 
    condition={(selector,state) => selector(state, 'somefield') === 'somevalue'} />

Credits to François for pointing me to the right direction with the forms. Unfortunately with this solution you'll need to wrap each your fields in a variable to be able to pass in to <Field>'s component property. (but if someone knows a nicer way, pls share, I'm new to react)

jkrnak
  • 956
  • 6
  • 9
0

I had a similar issue but I was not able to get the label displayed with this solution, so I did a little changes to have the label displayed and to make it a bit more general.

import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { formValueSelector } from 'redux-form';
import {TextInput,FieldTitle} from 'admin-on-rest';
import { Field } from 'redux-form';


function mapStateToProps(state, props) {
    return {
        isDisplayed: props.condition(formValueSelector('record-form'),state),
    }
}

const ConditionalInput = ({ isDisplayed,source,label,resource,isRequired,component, ...rest }) => {
    if (isDisplayed) {
        return <Field name={source} 
                      component={component} 
                      label={(addLabel)?<FieldTitle
                            label={label}
                            source={source}
                            resource={resource}
                            isRequired={isRequired}/>:null} {...rest} />
    } else {
        return null;
    }        
}

ConditionalInput.defaultProps = {
    addLabel: true,
    isRequired : false
}

ConditionalInput.propTypes = {
    addField: PropTypes.bool.isRequired,
    elStyle: PropTypes.object,
    input: PropTypes.object,
    label: PropTypes.string,
    resource: PropTypes.string,
    source: PropTypes.string,
    isRequired:PropTypes.bool,
    component:PropTypes.object
};

export const GcConditionalInput = connect(mapStateToProps)(ConditionalInput);


...

//then it can be used like this
<SelectInput source="terminationStatus" optionText="label" optionValue="id" choices={terminationStatus} style={inlineBlockStyle} allowEmpty/>               
<GcConditionalInput source="terminationEnteredDate" component={DateInput} condition={(selector,state) => selector(state, 'terminationStatus') !== null}/>
<GcConditionalInput source="terminationComment" component={TextInput} condition={(selector,state) => selector(state, 'terminationStatus') !== null}/>