Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make filters work with custom input with 2 fields #817

Closed
gandrin opened this issue Jul 3, 2017 · 9 comments
Closed

Make filters work with custom input with 2 fields #817

gandrin opened this issue Jul 3, 2017 · 9 comments

Comments

@gandrin
Copy link

gandrin commented Jul 3, 2017

Hello,

I tried to create a "between" like filter with 2 DateInput :

import React, { Component, PropTypes } from 'react';
import { Field } from 'redux-form';
import { DateInput } from 'admin-on-rest';

class PeriodInput extends Component {
  render() {
    const styles = {
      row: {
        display: 'flex',
        flexDirection: 'row',
        alignItems: 'flex-start',
        justifyContent: 'space-between',
      },
    };

    return (
      <span style={styles.row}>
        <Field
          name={this.props.fromSource}
          label={this.props.fromLabel}
          component={DateInput}
        />
        &nbsp;
        <Field
          name={this.props.toSource}
          label={this.props.toLabel}
          component={DateInput}
        />
      </span>
    );
  }
}

PeriodInput.propTypes = {
  fromSource: PropTypes.string.isRequired,
  fromLabel: PropTypes.string.isRequired,
  toSource: PropTypes.string.isRequired,
  toLabel: PropTypes.string.isRequired,
  label: PropTypes.string.isRequired,
};

PeriodInput.defaultProps = {
  addLabel: true,
  label: 'Position',
};

export default PeriodInput;
import React, { Component } from 'react';
import {
  List,
  Datagrid,
  TextField,
  ReferenceField,
  DateField,
  NumberField,
  ReferenceManyField,
  ChipField,
  SingleFieldList,
  EditButton,
  Filter,
  DateInput,
} from 'admin-on-rest';
import PeriodInput from '../common/periodInput';

export default class MoveList extends Component {

  render() {
    const MoveFilter = props => (
      <Filter {...props}>
        <DateInput label="resources.moves.fields.endDate_gte" source="endDate_gte" />
        <DateInput label="resources.moves.fields.startDate_lte" source="startDate_lte" />
        <PeriodInput
          source="between"
          fromLabel="resources.moves.fields.endDate_intersect_1"
          fromSource="endDate_intersect_1_gte"
          toLabel="resources.moves.fields.startDate_intersect_1"
          toSource="startDate_intersect_1_lte"
          label="resources.moves.filters.intersect"
        />
      </Filter>
    );
    return (
      <List filters={<MoveFilter />} {...this.props}>
        <Datagrid>
          <DateField label="resources.moves.fields.startDate" source="startDate" />
          <DateField label="resources.moves.fields.endDate" source="endDate" />
          <TextField label="resources.moves.fields.name" source="name" />
          <ReferenceField label="resources.moves.fields.zone" source="zoneId" reference="zones" linkType={false}>
            <TextField source="name" />
          </ReferenceField>
          <NumberField label="resources.moves.fields.id" source="id" />
          <ReferenceManyField perPage={5} label="resources.moves.fields.users" target="moveId" reference="movessgusers">
            <SingleFieldList>
              <ReferenceField source="sgUserId" reference="sgusers" linkType={false}>
                <ChipField source="email" />
              </ReferenceField>
            </SingleFieldList>
          </ReferenceManyField>
          <EditButton />
        </Datagrid>
      </List>
    );
  }
}

The filter works, when I choose it from the filters list, the two fields display well, I can choose dates and filter as expected. But when I use the cross to clear the filter, it does disapear, but it's not cleared as wanted. Clicking again on the filter in the filters list, display the filter with the previsously chosen values.

Thanks for your help !

PS: you always need to set the source property for the input in filters if you want to avoid a hideFilter console error.

@chemitaxis
Copy link

Same here

@fzaninotto
Copy link
Member

I'm surprised it even works the first time - the filter system wasn't designed to support combined filters. I consider it an enhancement request.

@fzaninotto fzaninotto changed the title Filters not working with custom input with 2 fields Make filters work with custom input with 2 fields Jul 4, 2017
@gandrin
Copy link
Author

gandrin commented Jul 5, 2017

@fzaninotto Yes, after looking at the filters code, I was surprised too.
I was expecting it to work though, since the documentation specify filters expect an input field and the example for custom input fields is actually composed by 2 fields.

@vedmalex
Copy link
Contributor

Hi.
@gandrin this specific feature is working. I've just test it use the following code.

    <PeriodInput
      source="between"
      fromLabel="from"
      fromSource="between.gte"
      toLabel="to"
      toSource="between.lte"
      label="period"
    />

you can use more than one field. and this is the solution i've searching for.

so the code remove the filter name as mentioned in source.
so if you just rename fields, remove filter button will work fine.
but also we can just replace

    return (
      <span style={styles.row}>
        <Field
          name={`${this.props.source}.gte`} //<<<
          label={this.props.fromLabel}
          component={DateInput}
        />
        &nbsp;
        <Field
          name={`${this.props.source}.lte`}//<<<
          label={this.props.toLabel}
          component={DateInput}
        />
      </span>
    );

@wmwart
Copy link

wmwart commented Nov 23, 2018

Hi.
my implementation:

//DateBetweenInput.js
import React, { Component } from 'react';
import { DateInput } from 'react-admin';
import { addField } from 'ra-core';

class DateBetweenInput extends Component {
  render() {
    const styles = {
      row: {
        display: 'flex',
        flexDirection: 'row',
        alignItems: 'flex-start',
        justifyContent: 'space-between',
      },
    };

    const {resource, source } = this.props

    return (
     <span style={styles.row}>
        <DateInput 
          source={`${source}_gte`}
          label={`resources.${resource}.fields.${source}_gte`}
        />
        &nbsp;
        <DateInput 
          source={`${source}_lte`}
          label={`resources.${resource}.fields.${source}_lte`}
        />
      </span>
    );
  }
}

DateBetweenInput.defaultProps = {
  addLabel: true,
  label: 'createdAt',
};

export default addField(DateBetweenInput);
//index.js
...

const OrderFilter = (props) => (
    <Filter {...props}>
        <SearchInput source="q" alwaysOn />
        <DateBetweenInput source="createdAt" />
    </Filter>

...

I'm new, and I have a few questions:

  • why is the time sent to the server in UTC when using DateTimeInput in the Edit component, but when using DateTimeInput in the Filter component, the time is sent to LocalTime?
  • what about the clear filter button? Do you plan to transfer to ListController.hideFilter() multiple sources?

@ghost
Copy link

ghost commented Feb 12, 2019

Thanks @wmwart , I extracted this to a reusable component:

import React from "react";

const styles = {
  row: {
    display: "flex",
    flexDirection: "row",
    alignItems: "flex-start",
    justifyContent: "space-between",
  },
};

export class InputRow extends React.PureComponent {
  render() {
    const { children, ...rest } = this.props;
    return (
      <span style={styles.row}>
        {React.Children.map(children, child => {
          return React.cloneElement(child, rest);
        })}
      </span>
    );
  }
}

and now you can just wrap any inputs with it:

<InputRow>
  <TextInput source="field1" />
  <TextInput source="field2" />
</InputRow>

@b-raines
Copy link
Contributor

Still seems this doesn't work to clear the combined input filter? Anyone found a solution to this yet?

@Kmaschta Kmaschta added the old label Oct 9, 2019
@andion
Copy link

andion commented Nov 13, 2020

In case somebody is still stuck clearing custom or multiple filters:

First, be careful with @waynebloss solution based on @wmwart's. It won't put labels on the filter dropdown because you are missing the source on the main component, wich RA's FilterButton uses to put labels on the dropdown. You can work around this by setting source on the InputRow and not using it on children like this

<MultipleFilter source="nameOfTheFilter">
  <Textinput source="nameOfTheFilter_lt" />
  <Textinput source="nameOfTheFilter_gt" />
</MultipleFilter>

// ... In your component 

const MultipleFilter = ({ children, source, ...rest }) => (
  <span style={styles.row}>
    {React.Children.map(children, (child) => {
      return React.cloneElement(child, rest);
    })}
  </span>
);

But RA does not know to clear those inputs, it will only clear the "aggregated source name" and let others as they are.

One idea could be just implementing your backend to ignore filters when there's no aggregated filter present. If you send "nameOfTheFilter", that has inside "nameOfTheFilter_gt" and "nameOfTheFilter_lt" just don't filter by any of them unless "nameOfTheFilter" is present.

The only "workaround" I found without backend code is implement everything as custom, from the input to the filters and filter consolidation system, and it's not worth it, you'd be better not using React Admin at all.

@fzaninotto
Copy link
Member

This is now possible thanks to StackedFilters

https://marmelab.com/react-admin/StackedFilters.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants