multiple MDBSelect does not select from state


Topic: multiple MDBSelect does not select from state

hberndt asked 6 years ago

<MDBSelect multiple>
   <MDBSelectInput id="authorities" 
                   name="authorities" 
                   selected={this.state.authorities} 
                   onChange={this.changeHandler}/>
   <MDBSelectOptions>
       {roles.map(role => (
       <MDBSelectOption value={role}>
           {role}
       </MDBSelectOption>
       ))}
   </MDBSelectOptions>
</MDBSelect>

The option within the array this.state.authorities are not checked. I cannot find my mistake. Singel MDBSelect works and if I check some values the array gets set correctly.


Anna Morawska staff answered 6 years ago

Hi there,

I'm not sure what you want to achieve - please provide me with a complete working example, with state object and the changeHandler method, then I will try to help :)

Best,

Ania


hberndt answered 6 years ago

Ok, thank you

State object

state: IUserManagementUpdateState = {
  isNew: !this.props.match.params || !this.props.match.params.login,
  isShow: !this.props.match.url.endsWith('edit'),
  activeItem: '1',
  value: {}
};

value is an entity which get loaded via redux. It is set within render:

const { user : propsUser, loading, updating, roles } = this.props;
const { value : stateUser = {} } = this.state;

const current = defaults({}, stateUser, propsUser);

The entity has a member authorities as array (e.g. ROLE_ADMIN, ROLE_USER)

Now comes the snippet for the select box:

<MDBSelect multiple>
   <MDBSelectInput id="authorities" name="authorities" 
                   selected={current.authorities} 
                   disabled={this.state.isShow}/>
      <MDBSelectOptions
                {roles.map(role => ( 
                      <MDBSelectOption value={role} checked>
                            {role}
                      </MDBSelectOption>
                ))}
      </MDBSelectOptions>
</MDBSelect>

The options are not selected. For completion here is my changeHandler:

changeHandler = e => {
  const newValues = getValueFromEvent(e);
  console.log(this.state.value);
  if (newValues) {
      const change = {
          ...this.state.value,
          ...newValues
      };

      // Update our component-local state with these changes, so that the child components
      // will re-render with the new values right away
      this.setState({ value: change });
  }
};

Anna Morawska staff answered 6 years ago

well, still not sure If I understand you ;p You use that component in a really unusual way - you don't have to manually update state to check and uncheck select options - it is done internally.

Does the changeHandler method fire?

Please, take a look at this example:

import React, { Component } from "react";
import { MDBSelect, MDBSelectInput, MDBSelectOptions, MDBSelectOption} from "mdbreact";

class SelectPage extends Component {

  changeHandler = e => {
   console.log(e)
  };


  render() {
    return (
      <div>
        <MDBSelect multiple getValue={this.changeHandler}>
          <MDBSelectInput selected="Choose your option"  />
          <MDBSelectOptions>
            <MDBSelectOption disabled>Choose your option</MDBSelectOption>
            <MDBSelectOption value="1" >Option 1</MDBSelectOption>
            <MDBSelectOption value="2">Option 2</MDBSelectOption>
            <MDBSelectOption value="3">Option 3</MDBSelectOption>
            <MDBSelectOption value="4" selected>Option 4</MDBSelectOption>
            <MDBSelectOption value="5">Option 5</MDBSelectOption>
          </MDBSelectOptions>
        </MDBSelect>
      </div>
    );
  }
}

export default SelectPage;

In my opinion you should refactor your code, then you will get something similar to this:

import React, { Component } from "react";
import { MDBSelect, MDBSelectInput, MDBSelectOptions, MDBSelectOption} from "mdbreact";

class SelectPage extends Component {

  changeHandler = e => {
   console.log(e)
  };


  render() {
    return (
      <div>
        <MDBSelect multiple getValue={this.changeHandler}>
          <MDBSelectInput d="authorities" name="authorities" selected={current.authorities} disabled={this.state.isShow}   />
          <MDBSelectOptions>
            <MDBSelectOption disabled>Choose your option</MDBSelectOption>
           { roles.map(role => (
            <MDBSelectOption value={role}>{role}</MDBSelectOption>
            ))
            }
        </MDBSelectOptions>
        </MDBSelect>
      </div>
    );
  }
}

export default SelectPage;

hberndt answered 6 years ago

Hi Anna, here is my update. I implemented your solution. There seems to be a bug with multiple selection. Without the multiple option, the first option in the array is displayed. With multiple option, the array gets cleared.

changeHandlerRoles = value => {
  let newValues;
  newValues = {
    authorities: value
  };
  const change = {
      ...this.state.value,
      ...newValues
  };
  console.log(change);
  this.setState({ value: change });
}

<MDBSelect multiple getValue={this.changeHandlerRoles}>
    <MDBSelectInput id="authorities" name="authorities" selected={current.authorities}/>
        <MDBSelectOptions>
            {roles.map(role => (
            <MDBSelectOption key={role} value={role}>{role}</MDBSelectOption>
            ))}
        </MDBSelectOptions>
</MDBSelect>

The console first logs an empty array while rendering when getValue is triggered. Would be great to get a solution. Regards, Holger


Anna Morawska staff answered 6 years ago

Hi there,

please check out this solution - it use the map method on the roles array and renders options. You can choose which option has to be checked at the beginning.

import React, { Component } from "react";
import { MDBSelect, MDBSelectInput, MDBSelectOptions, MDBSelectOption } from "mdbreact";

class SelectPage extends Component {

  state = {
    current: 'admin',
    roles: [{name: "admin", selected: false}, {name: "moderator", selected: true}, {name: "developer", selected: false}, {name: "user", selected: false} ],
    authorities: ""
  }

  changeHandlerRoles = value => {
    this.setState({ authorities: value }, () => console.log(this.state));
  }


  render(){

    const { roles } = this.state;

    return(
      <MDBSelect multiple getValue={this.changeHandlerRoles} >
        <MDBSelectInput id="authorities" name="authorities" />
          <MDBSelectOptions>
              {roles.map(role => (
              <MDBSelectOption key={role.name} value={role.name} selected={role.selected}>{role.name}</MDBSelectOption>
              ))}
          </MDBSelectOptions>
      </MDBSelect>
    )
  }
}

export default SelectPage;

Raiczyk commented 6 years ago

Hi Anna, can you help me with my post ?? Thanks!


Raiczyk answered 6 years ago

Hi! I'm having the same problem. The proposed solution works, but it's not the best option in my case. I define the state:

...
constructor(props) {
    super(props);
    this.state = {
        currentFilters: {
            provinces: [],
            localities: [],
            stores: [],
            dates: {
                from: null,
                to: null
            },
            sites: []
        }
    };
    this.handleSelectChange = this.handleSelectChange.bind(this)
}

and then bind it to the select like this:

            <MDBSelect getValue={values => this.handleSelectChange('stores', values)} multiple>
                <MDBSelectInput selected={this.state.currentFilters.stores} />
                <MDBSelectOptions search>
                    {
                        this.props.availableStores.map(store =>
                            <MDBSelectOption value={store.id}>{store.name}</MDBSelectOption>)
                    }
                </MDBSelectOptions>
            </MDBSelect>

then every time render is called, the select should show currentFilters.stores as selected options because it's binded to the state.

Is that possible?

Thanks in advance.


Anna Morawska staff answered 6 years ago

Hi there,

somehow, you have to decide if the store is selected - so maybe write a separate function which, based on passed props, return true or false according to the curretFilters value. For example something like this:

 checkIfSelected = store => () => {
const { provinces, localities } = this.state.currentFilters;

if (provinces.includes(store.province)) return true;

if (localities.includes(store.localisation)) return true;

return false;

}

 <MDBSelect getValue={values => this.handleSelectChange('stores', values)} multiple>
    <MDBSelectInput selected="Choose your option" />
    <MDBSelectOptions search>
      {
        this.props.availableStores.map(store =>
          <MDBSelectOption selected={this.checkIfSelected(store)} value={store.id}>{store.name}</MDBSelectOption>)
      }
    </MDBSelectOptions>
  </MDBSelect>
)

Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Answered

Specification of the issue
  • User: Free
  • Premium support: No
  • Technology: MDB React
  • MDB Version: 4.8.4
  • Device: Notebook
  • Browser: chrome
  • OS: macOS
  • Provided sample code: No
  • Provided link: No