본문 바로가기
개발/React

React Material - Auto Complete로 목록 관리하기 (Show Autocomplete Drop-Down List)

by 피로물든딸기 2023. 6. 18.
반응형

리액트 전체 링크

 

참고

- mui autocomplete

 

위의 링크를 참고하여 아래의 예시에서 시작한다.

import React, { useState } from "react";

import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";
import Stack from "@mui/material/Stack";

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

const MyAutoComplete = () => {
  const defaultProps = {
    options: top100Films,
    getOptionLabel: (option) => option.title,
  };

  const [value, setValue] = useState(null);

  return (
    <div>
      <p>{value ? value.title : null}</p>
      <Stack spacing={1} sx={{ width: 300, marginLeft: 5 }}>
        <Autocomplete
          {...defaultProps}
          id="auto-complete"
          autoComplete
          includeInputInList
          value={value}
          onChange={(event, newValue) => {
            setValue(newValue);
          }}
          renderInput={(params) => (
            <TextField {...params} label="목록" variant="standard" />
          )}
        />
      </Stack>
    </div>
  );
};

export default MyAutoComplete;

 

top100Films의 Title이 순서대로 출력되는 것을 알 수 있다.


목록 라벨 변경하기

 

목록의 라벨을 변경하려면 getOptionLabel을 수정하면 된다.

top100Films에 있는 정보인 year도 같이 출력하도록 해보자.

  const defaultProps = {
    options: top100Films,
    getOptionLabel: (option) => `${option.year} : ${option.title}`,
  };

 

그러면 year : title 형식으로 목록이 변경된다.


카테고리, 그룹화

 

year : title 형식보다는 연도별로 영화 제목을 정리하는 것이 더 깔끔해보인다.

Autocomplete에는 그룹화 기능이 제공된다.

 

먼저 defaultProps 삭제하고 아래와 같이 코드를 추가하자.

options에서 top100Films를 연도가 작은 순서대로 정렬하였다.

그리고 groupBy를 이용해 연도별로 영화를 묶는다.

getOptionLabel은 다시 title만 출력하도록 하였다.

      <Stack spacing={1} sx={{ width: 300, marginLeft: 5 }}>
        <Autocomplete
          /* group */
          options={ top100Films.sort((a,b) => (a.year - b.year))}
          groupBy={(option) => option.year}
          getOptionLabel={(option) => option.title}
          
          id="auto-complete"
          autoComplete
          includeInputInList
          value={value}
          onChange={(event, newValue) => {
            setValue(newValue);
          }}
          renderInput={(params) => (
            <TextField {...params} label="목록" variant="standard" />
          )}
        />
      </Stack>

 

연도별로 영화 타이틀이 카테고리화 되었지만, 너무 세분화되어서 정신이 없어보인다.

 

그러므로 10년 단위로 그룹을 나눠보자.

top100Films에서 year10 변수를 추가하여 top100Films10Year를 만든다.

(ex. year : 1931이라면 year10은 1930이 된다.)

 

그리고 options에서 year10 기준으로 정렬, groupByyear10으로 그룹을 묶는다.

  const top100Films10Year = top100Films.map((item) => {
    const year10 = item.year - (item.year % 10);
    return {
      ...item,
      year10,
    };
  });

  return (
    <div>
      <p>{value ? value.title : null}</p>
      <Stack spacing={1} sx={{ width: 300, marginLeft: 5 }}>
        <Autocomplete
          /* group */
          options={ top100Films10Year.sort((a,b) => (a.year10 - b.year10))}
          groupBy={(option) => option.year10}
          getOptionLabel={(option) => option.title}

          id="auto-complete"
          autoComplete
          includeInputInList
          value={value}
          onChange={(event, newValue) => {
            setValue(newValue);
          }}
          renderInput={(params) => (
            <TextField {...params} label="목록" variant="standard" />
          )}
        />
      </Stack>
    </div>
  );

 

다음과 같이 10년 단위로 그룹이 나눠지게 되어 조금 더 깔끔해졌다.


필터링 옵션

 

기본적으로 Autocomplete가 제공하는 필터링 옵션 대소문자를 구분하지 않고 입력한 문자열을 포함하는 것이다.

 

이제 대소문자를 구분하도록 직접 myFilter를 구현해보자.

filterOptions={(list, input) ... } 에서 list는 현재 Autocomplete의 목록임을 알 수 있다.

그리고 input의 inputValue에 현재 입력된 값이 들어있다.

 

myFilter에서 inputValue가 없는 경우는 모두 출력하고, 그 외에는 해당 문자열을 포함하는 경우만 출력한다.

여기서 대소문자를 구분하지 않는 로직이 없으므로, 정확히 입력된 값을 포함하는 경우만 필터링된다.

  const myFilter = (item, inputValue) => {
    if (inputValue === "") return true;
    return item.title.includes(inputValue);
  };
  
    return (
    <div>
      <p>{value ? value.title : null}</p>
      <Stack spacing={1} sx={{ width: 300, marginLeft: 5 }}>
        <Autocomplete
          /* group */
          options={top100Films10Year.sort((a, b) => a.year10 - b.year10)}
          groupBy={(option) => option.year10}
          getOptionLabel={(option) => option.title}

          /* filter */
          filterOptions={(list, input) =>
            list.filter((item) => myFilter(item, input.inputValue))
          }

		  ...
          renderInput={(params) => (
            <TextField {...params} label="목록" variant="standard" />
          )}
        />
      </Stack>
    </div>
  );

 

위와 같이 바뀐 경우 "the"가 포함된 목록만 나타나며, "The"만 있는 경우는 볼 수 없게 된다.


다중 선택

 

여러 옵션을 출력하기 위해 useState로 변수를 추가하자.

const [multipleValue, setMultipleValue] = useState([]);

 

multiple과 filterSelectedOptions 옵션을 추가하면 여러 옵션을 선택할 수 있다.

  <Autocomplete
    multiple
    id="tags-outlined"
    options={top100Films}
    getOptionLabel={(option) => option.title}
    //defaultValue={[top100Films[13]]}
    filterSelectedOptions
    renderInput={(params) => (
      <TextField
        {...params}
        label="filterSelectedOptions"
        placeholder="Favorites"
      />
    )}
    onChange={(event, newValue) => {
      const util = require("util");
      let str = newValue.map((data) => {
        if (typeof data === "object") {
          return util.inspect(data);
        }
        return data;
      });

      console.log(str);
      setMultipleValue(str);
    }}
  />
  <p>{multipleValue}</p>

 

전체 코드는 다음과 같다.

import React, { useState } from "react";

import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";
import Stack from "@mui/material/Stack";

const top100Films = [
  { title: "The Shawshank Redemption", year: 1994 },
  { title: "The Godfather", year: 1972 },
  { title: "The Godfather: Part II", year: 1974 },
  { title: "The Dark Knight", year: 2008 },
  { title: "12 Angry Men", year: 1957 },
  { title: "Schindler's List", year: 1993 },
  { title: "Pulp Fiction", year: 1994 },
  {
    title: "The Lord of the Rings: The Return of the King",
    year: 2003,
  },
  { title: "The Good, the Bad and the Ugly", year: 1966 },
  { title: "Fight Club", year: 1999 },
  {
    title: "The Lord of the Rings: The Fellowship of the Ring",
    year: 2001,
  },
  {
    title: "Star Wars: Episode V - The Empire Strikes Back",
    year: 1980,
  },
  { title: "Forrest Gump", year: 1994 },
  { title: "Inception", year: 2010 },
  {
    title: "The Lord of the Rings: The Two Towers",
    year: 2002,
  },
  { title: "One Flew Over the Cuckoo's Nest", year: 1975 },
  { title: "Goodfellas", year: 1990 },
  { title: "The Matrix", year: 1999 },
  { title: "Seven Samurai", year: 1954 },
  {
    title: "Star Wars: Episode IV - A New Hope",
    year: 1977,
  },
  { title: "City of God", year: 2002 },
  { title: "Se7en", year: 1995 },
  { title: "The Silence of the Lambs", year: 1991 },
  { title: "It's a Wonderful Life", year: 1946 },
  { title: "Life Is Beautiful", year: 1997 },
  { title: "The Usual Suspects", year: 1995 },
  { title: "Léon: The Professional", year: 1994 },
  { title: "Spirited Away", year: 2001 },
  { title: "Saving Private Ryan", year: 1998 },
  { title: "Once Upon a Time in the West", year: 1968 },
  { title: "American History X", year: 1998 },
  { title: "Interstellar", year: 2014 },
  { title: "Casablanca", year: 1942 },
  { title: "City Lights", year: 1931 },
  { title: "Psycho", year: 1960 },
  { title: "The Green Mile", year: 1999 },
  { title: "The Intouchables", year: 2011 },
  { title: "Modern Times", year: 1936 },
  { title: "Raiders of the Lost Ark", year: 1981 },
  { title: "Rear Window", year: 1954 },
  { title: "The Pianist", year: 2002 },
  { title: "The Departed", year: 2006 },
  { title: "Terminator 2: Judgment Day", year: 1991 },
  { title: "Back to the Future", year: 1985 },
  { title: "Whiplash", year: 2014 },
  { title: "Gladiator", year: 2000 },
  { title: "Memento", year: 2000 },
  { title: "The Prestige", year: 2006 },
  { title: "The Lion King", year: 1994 },
  { title: "Apocalypse Now", year: 1979 },
  { title: "Alien", year: 1979 },
  { title: "Sunset Boulevard", year: 1950 },
  {
    title:
      "Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb",
    year: 1964,
  },
  { title: "The Great Dictator", year: 1940 },
  { title: "Cinema Paradiso", year: 1988 },
  { title: "The Lives of Others", year: 2006 },
  { title: "Grave of the Fireflies", year: 1988 },
  { title: "Paths of Glory", year: 1957 },
  { title: "Django Unchained", year: 2012 },
  { title: "The Shining", year: 1980 },
  { title: "WALL·E", year: 2008 },
  { title: "American Beauty", year: 1999 },
  { title: "The Dark Knight Rises", year: 2012 },
  { title: "Princess Mononoke", year: 1997 },
  { title: "Aliens", year: 1986 },
  { title: "Oldboy", year: 2003 },
  { title: "Once Upon a Time in America", year: 1984 },
  { title: "Witness for the Prosecution", year: 1957 },
  { title: "Das Boot", year: 1981 },
  { title: "Citizen Kane", year: 1941 },
  { title: "North by Northwest", year: 1959 },
  { title: "Vertigo", year: 1958 },
  {
    title: "Star Wars: Episode VI - Return of the Jedi",
    year: 1983,
  },
  { title: "Reservoir Dogs", year: 1992 },
  { title: "Braveheart", year: 1995 },
  { title: "M", year: 1931 },
  { title: "Requiem for a Dream", year: 2000 },
  { title: "Amélie", year: 2001 },
  { title: "A Clockwork Orange", year: 1971 },
  { title: "Like Stars on Earth", year: 2007 },
  { title: "Taxi Driver", year: 1976 },
  { title: "Lawrence of Arabia", year: 1962 },
  { title: "Double Indemnity", year: 1944 },
  {
    title: "Eternal Sunshine of the Spotless Mind",
    year: 2004,
  },
  { title: "Amadeus", year: 1984 },
  { title: "To Kill a Mockingbird", year: 1962 },
  { title: "Toy Story 3", year: 2010 },
  { title: "Logan", year: 2017 },
  { title: "Full Metal Jacket", year: 1987 },
  { title: "Dangal", year: 2016 },
  { title: "The Sting", year: 1973 },
  { title: "2001: A Space Odyssey", year: 1968 },
  { title: "Singin' in the Rain", year: 1952 },
  { title: "Toy Story", year: 1995 },
  { title: "Bicycle Thieves", year: 1948 },
  { title: "The Kid", year: 1921 },
  { title: "Inglourious Basterds", year: 2009 },
  { title: "Snatch", year: 2000 },
  { title: "3 Idiots", year: 2009 },
  { title: "Monty Python and the Holy Grail", year: 1975 },
];

const MyAutoComplete = () => {
  // const defaultProps = {
  //   options: top100Films,
  //   getOptionLabel: (option) => `${option.year} : ${option.title}`,
  // };

  const [value, setValue] = useState(null);
  const [multipleValue, setMultipleValue] = useState([]);

  const top100Films10Year = top100Films.map((item) => {
    const year10 = item.year - (item.year % 10);
    return {
      ...item,
      year10,
    };
  });

  const myFilter = (item, inputValue) => {
    if (inputValue === "") return true;
    return item.title.includes(inputValue);
  };

  return (
    <div>
      <p>{value ? value.title : null}</p>
      <Stack spacing={1} sx={{ width: 300, marginLeft: 5 }}>
        <Autocomplete
          /* group */
          options={top100Films10Year.sort((a, b) => a.year10 - b.year10)}
          groupBy={(option) => option.year10}
          getOptionLabel={(option) => option.title}

          /* filter */
          filterOptions={(list, input) =>
            list.filter((item) => myFilter(item, input.inputValue))
          }

          id="auto-complete"
          autoComplete
          includeInputInList
          value={value}
          onChange={(event, newValue) => {
            setValue(newValue);
          }}
          renderInput={(params) => (
            <TextField {...params} label="목록" variant="standard" />
          )}
        />
      </Stack>
      <hr style={{borderColor: "black"}} />
      <Autocomplete
        multiple
        id="tags-outlined"
        options={top100Films}
        getOptionLabel={(option) => option.title}
        //defaultValue={[top100Films[13]]}
        filterSelectedOptions
        renderInput={(params) => (
          <TextField
            {...params}
            label="filterSelectedOptions"
            placeholder="Favorites"
          />
        )}
        onChange={(event, newValue) => {
          const util = require("util");
          let str = newValue.map((data) => {
            if (typeof data === "object") {
              return util.inspect(data);
            }
            return data;
          });

          console.log(str);
          setMultipleValue(str);
        }}
      />
      <p>{multipleValue}</p>
    </div>
  );
};

export default MyAutoComplete;

 

결과는 링크에서 확인하자.

반응형

댓글