softwareUser
softwareUser

Reputation: 448

React JS Material UI Autocomplete: Change Options

I want to use an Autocomplete field for my React JS Project. For the design of the UI I use Material UI. In the documentation you can see the following example:

<Autocomplete
                    required
                    id="combo-box-demo"
                    filterOptions={(x) => x}
                    value={this.state.departure}
                    options={top100Films}
                    getOptionLabel={(option) => option.title}
                    renderInput={(params) => <TextField {...params} label="Startpunkt" variant="outlined" />}
                />

The options objects have the following default value:

let top100Films = [
        { title: 'The Shawshank Redemption', year: 1994 },
        { title: 'Monty Python and the Holy Grail', year: 1975 },
    ];

For my purpose I want to dynamically change the options since I use an Rest API where I get the results for the input. My question is therefore how I can change the options dynamically when the user is typing.

Upvotes: 6

Views: 16911

Answers (3)

thenora
thenora

Reputation: 19

I'm doing this as part of an address search/verification by using the OnChange in the text field with a handleAddressChange function that calls a findAddresses function. findAddresses uses Axios to make a call to an API, and then saves those results and displays them as the options for the results in the autocomplete.

Here's a simplified version of my code:

import React, { useState, ChangeEvent } from 'react';
import {
  TextField,
  InputAdornment
} from "@material-ui/core";
import Autocomplete from '@material-ui/lab/Autocomplete';
import { Search } from "@material-ui/icons";
import axios from "axios";

const AddressSearch = (props) => {

  const [addressesList, setAddressesList] = useState([]);
  const [inputAddress, setInputAddress] = useState<string>("");

  const handleAddressChange = (event: ChangeEvent<{ value: unknown }>) => {
    setInputAddress(event.target.value as string);
    findAddresses(event.target.value as string);
  };

  const baseUrl = 'https://api.website.com/';

  const findAddresses = (text?: string) => {
    let params = `Key=value`
    if (!!text) {
      params += (`&Text=` + text);
      let addressesResponse;

      return (
        axios.get(baseUrl + params)
          .then(response => {
            addressesResponse = response.data.Items
            if (!Array.isArray(addressesResponse) || !addressesResponse.length) {
              return;
            }
            setAddressesList(addressesResponse);
          })
          .catch(error => console.log(error))
      )
    }
  }

  return (
    <div>
      <Autocomplete
        id="address-autocomplete"
        freeSolo
        options={addressesList}
        getOptionLabel={(option) => option.Text}
        popupIcon={<Search />}
        renderInput={(params) => <TextField 
                                    id="address-input"                          
                                    {...params} 
                                    onChange={handleAddressChange}
                                    placeholder="Quickly find your address" 
                                    InputProps={{ ...params.InputProps,
                                      startAdornment: (
                                        <InputAdornment position="start"><Search /></InputAdornment>
                                      )
                                    }}
                                  /> }
      />
    </div>
  );
}

export default AddressSearch;

Upvotes: -1

Arfina Arfwani
Arfina Arfwani

Reputation: 176

You can use onInputChange prop in your case:

      <Autocomplete
            required
            id='combo-box-demo'
            filterOptions={(x) => x}
            value={this.state.departure}
            options={top100Films}
            getOptionLabel={(option) => option.title}
            onInputChange={(event: object, value: string, reason: string) => {
              if (reason === 'input') {
                changeOptionBaseOnValue(value);
              }
            }}
            renderInput={(params) => (
              <TextField {...params} label='Startpunkt' variant='outlined' />
            )}
          />

Then you can define changeOptionBaseOnValue to handle your options.

Upvotes: 8

Khabir
Khabir

Reputation: 5844

You can check this example:

import fetch from 'cross-fetch';
import React from 'react';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import CircularProgress from '@material-ui/core/CircularProgress';

function sleep(delay = 0) {
  return new Promise((resolve) => {
    setTimeout(resolve, delay);
  });
}

export default function Asynchronous() {
  const [open, setOpen] = React.useState(false);
  const [options, setOptions] = React.useState([]);
  const loading = open && options.length === 0;

  React.useEffect(() => {
    let active = true;

    if (!loading) {
      return undefined;
    }

    (async () => {
      const response = await fetch('https://country.register.gov.uk/records.json?page-size=5000');
      await sleep(1e3); // For demo purposes.
      const countries = await response.json();

      if (active) {
        setOptions(Object.keys(countries).map((key) => countries[key].item[0]));
      }
    })();

    return () => {
      active = false;
    };
  }, [loading]);

  React.useEffect(() => {
    if (!open) {
      setOptions([]);
    }
  }, [open]);

  return (
    <Autocomplete
      id="asynchronous-demo"
      style={{ width: 300 }}
      open={open}
      onOpen={() => {
        setOpen(true);
      }}
      onClose={() => {
        setOpen(false);
      }}
      getOptionSelected={(option, value) => option.name === value.name}
      getOptionLabel={(option) => option.name}
      options={options}
      loading={loading}
      renderInput={(params) => (
        <TextField
          {...params}
          label="Asynchronous"
          variant="outlined"
          InputProps={{
            ...params.InputProps,
            endAdornment: (
              <React.Fragment>
                {loading ? <CircularProgress color="inherit" size={20} /> : null}
                {params.InputProps.endAdornment}
              </React.Fragment>
            ),
          }}
        />
      )}
    />
  );
}

Source

Upvotes: -1

Related Questions