OnlineShop 만들기 - 6) Landing Page 만들기 3(check 필터링, price 필터링 만들기)
체크한 나라에 관한 (continents) 자료들만 로드되도록 한다.
checkbox에 관한 내용들은 sections폴더안에 따로 생성한다.
src>components>views>LandingPage>Sections>CheckBox.js파일을 생성한다.
import React from 'react'
import { Checkbox, Collapse } from 'antd';
const continents = [
{
"_id": 1,
"name": "Africa"
},
{
"_id": 2,
"name": "Europe"
},
{
"_id": 3,
"name": "Asia"
},
{
"_id": 4,
"name": "North America"
},
{
"_id": 5,
"name": "South America"
},
{
"_id": 6,
"name": "Australia"
},
{
"_id": 7,
"name": "Antarctica"
}
]
function CheckBox() {
return (
<div>
<Collapse defaultActiveKey={['0']} >
<Panel header key="1">
<React.Fragment key={index}>
<Checkbox
onChange
type
checked
/>
<span>{value.name}</span>
</React.Fragment>
))
</Panel>
</Collapse>
</div>
)
}
랜딩페이지안에도 넣어준다.
import CheckBox from './Sections/CheckBox';
{/* Filter */}
<CheckBox />
onChange Function을 만들어보자.
import React ,{useState} from 'react'
import { Checkbox, Collapse } from 'antd';
function CheckBox(props) {
const [Checked, setChecked] = useState([])//어떤걸 체크했는지 배열에 넣어서 알려준다.
const handleToggle = (value) => {//어떤 체크박스를 넣어줄건지 value를 준다.
const currentIndex = Checked.indexOf(value);//체크된 것의 인덱스를 가져온다.
const newChecked = [...Checked]; //체크된것들
if (currentIndex === -1) {//체크가 안되어 있었으면
newChecked.push(value)//가져올 checked에 값을 추가한다.
} else {//이미 체크가 되어있으면
newChecked.splice(currentIndex, 1)//체크에서 제외해준다.
}
setChecked(newChecked)//새로 체크 변화된 것 다시 한번 세팅
props.handleFilters(newChecked)//변화된것 부모에게 전달해준다.
//update this checked information into Parent Component
}
const renderCheckboxLists = () => props.list && props.list.map((value, index) => (
<React.Fragment key={index}>
<Checkbox
onChange={() => handleToggle(value._id)}//체크 표시
type="checkbox"
checked={Checked.indexOf(value._id) === -1 ? false : true}//체크 여부(되지않으면 -1, 되어있으면 !=-1)에 따라서 checked 가 true, false
/>
<span>{value.name}</span>
</React.Fragment>
))
return (
<div>
<Collapse defaultActiveKey={['0']} >
<Panel header="Continents" key="1">
{renderCheckboxLists()}
</Panel>
</Collapse>
</div>
)
}
여기서 부모에게 전달해줄 handletoggle함수는 부모가 가지고 있어야 하므로
랜딩페이지에 구현해준다.
const [Filters, setFilters] = useState({//두가지의 필터를 가지고 있다.
continents: [],
price: []
})
const showFilteredResults = (filters) => {//필터링한 결과를 가지고 온다.
const variables = {
skip: 0,//체크박스를 새로 설정하면 처음부터 다시 보여줘야 한다.
limit: Limit,
filters: filters
}
getProducts(variables)//결과를 가져오기 위해서 getProducts사용한다.
setSkip(0)
}
const handleFilters = (filters, category) => {//체크된것들에 변화가 있으면 새로 받아오는 것(자식으로 부터 변화 받아오는 함수 )
//체크가 된다면 배열 안에 체크된 인덱스 번호가 추가될 것이다.
const newFilters = { ...Filters }//새로운 필터만들고
newFilters[category] = filters//해당카테고리의 필터는 입력된 filters가 될 것이다.
if (category === "price") {
}
console.log(newFilters)
//필터링한 결과를 세팅해준다.
showFilteredResults(newFilters)
//필터의 상태를 새로운 필터로 바꿔준다.
setFilters(newFilters)
}
{/* Filter */}
<CheckBox
handleFilters={filters => handleFilters(filters, "continents")}
/>
getproducts 라우터에서 필터링에 따라서 가져오는 종류가 달라질 수 있도록 수정해준다.
router.post("/getProducts", (req, res) => {
//variables가 있을때와 없을 때를 나눈다.
let order = req.body.order ? req.body.order : "desc";
let sortBy = req.body.sortBy ? req.body.sortBy : "_id";
let limit = req.body.limit ? parseInt(req.body.limit) : 100;
let skip = parseInt(req.body.skip);
let findArgs = {};
for (let key in req.body.filters) { //필터에 따라서
if (req.body.filters[key].length > 0) {
if (key === "price") {
}
} else {//키가 price가 아니라면
findArgs[key] = req.body.filters[key];//findArgs[continents] = [인덱스 배열]의 형태
}
}
}
Product.find(findArgs)
.populate("writer")
.sort([[sortBy, order]])
.skip(skip)
.limit(limit)
.exec((err, products) => {
if (err) return res.status(400).json({ success: false, err })
res.status(200).json({ success: true, products, postSize: products.length })
})
});
이제 가격에 의한 필터링을 만들어보자.
체크박스 component처럼 라디오 박스 component를 만들어준다.
src>component>views>Sections>RadioBox.js파일
import React from 'react'
import { Collapse, Radio } from 'antd';
const { Panel } = Collapse;
const price = [
{
"_id": 0,
"name": "Any",
"array": []
},
{
"_id": 1,
"name": "$0 to $199",
"array": [0, 199]
},
{
"_id": 2,
"name": "$200 to $249",
"array": [200, 249]
},
{
"_id": 3,
"name": "$250 to $279",
"array": [250, 279]
},
{
"_id": 4,
"name": "$280 to $299",
"array": [280, 299]
},
{
"_id": 5,
"name": "More than $300",
"array": [300, 1500000]
}
]
function RadioBox() {
const [Value, setValue] = useState('0')
const renderRadioBox = () => (
props.list && props.list.map((value) => (
<Radio key={value._id} value={`${value._id}`}>{value.name}</Radio>
))
)
return (
<div>
<Collapse defaultActiveKey={['0']}>
<Panel header="price" key="1">
<Radio.Group onChange value>
{renderRadioBox()}
</Radio.Group>
</Panel>
</Collapse>
</div>
)
}
export default RadioBox
랜딩페이지에도 추가해준다.
import RadioBox from './Sections/RadioBox';
{/* Filter */}
<RadioBox
handleFilters={filters => handleFilters(filters, "price")}
/>
antd를 이용해서 col, row디자인을 준다.
{/* Filter */}
<Row gutter={[16, 16]}>
<Col lg={12} xs={24} >
<CheckBox
list={continents}
handleFilters={filters => handleFilters(filters, "continents")}
/>
</Col>
<Col lg={12} xs={24}>
<RadioBox
list={price}
handleFilters={filters => handleFilters(filters, "price")}
/>
</Col>
</Row>
RadioBox 에 onChange Function을 만들어준다.
const [Value, setValue] = useState('0')//id가 0부터 시작하니까
const handleChange = (event) => {
setValue(event.target.value)
props.handleFilters(event.target.value)//부모컴포넌트로 보내준다.
}
return (
<div>
<Collapse defaultActiveKey={['0']}>
<Panel header="price" key="1">
<Radio.Group onChange={handleChange} value={Value}>
{renderRadioBox()}
</Radio.Group>
</Panel>
</Collapse>
</div>
)
}
handleFilter Function을 만들어보자.
먼저 가격정보랑 continents 정보를 담았던 CheckBox, RadioBox에서 정보들만 따로 모으자.
Sections>Datas.js파일을 만든다.
const price = [
{
"_id": 0,
"name": "Any",
"array": []
},
{
"_id": 1,
"name": "$0 to $199",
"array": [0, 199]
},
{
"_id": 2,
"name": "$200 to $249",
"array": [200, 249]
},
{
"_id": 3,
"name": "$250 to $279",
"array": [250, 279]
},
{
"_id": 4,
"name": "$280 to $299",
"array": [280, 299]
},
{
"_id": 5,
"name": "More than $300",
"array": [300, 1500000]
}
]
const continents = [
{
"_id": 1,
"name": "Africa"
},
{
"_id": 2,
"name": "Europe"
},
{
"_id": 3,
"name": "Asia"
},
{
"_id": 4,
"name": "North America"
},
{
"_id": 5,
"name": "South America"
},
{
"_id": 6,
"name": "Australia"
},
{
"_id": 7,
"name": "Antarctica"
}
]
export {
price,
continents
}
랜딩페이지에 둘다 임포트 해준다.
import { continents, price } from './Sections/Datas';
각각 CheckBox, RadioBox에는 props로 보내주자.
{/* Filter */}
<Row gutter={[16, 16]}>
<Col lg={12} xs={24} >
<CheckBox
list={continents}//filter보내준다.
handleFilters={filters => handleFilters(filters, "continents")}
/>
</Col>
<Col lg={12} xs={24}>
<RadioBox
list={price}//filter보내준다.
handleFilters={filters => handleFilters(filters, "price")}
/>
</Col>
</Row>
//각각 radiobox, checkbox도 props.list로 변경해준다.
const renderRadioBox = () => (
props.list && props.list.map((value) => (
<Radio key={value._id} value={`${value._id}`}>{value.name}</Radio>
))
)
const renderCheckboxLists = () => props.list && props.list.map((value, index) => (
랜딩페이지에서 handleFilters함수에 다음 내용을 추가한다.
const handlePrice = (value) => {//가격 조정함수
const data = price;
let array = [];
for (let key in data) {//데이터 안의 키들
if (data[key]._id === parseInt(value, 10)) {// key는 가격이 아니라 인덱스, value는 string으로 되어있는데 이를 int로 바꿔준다.
array = data[key].array;
}
}
console.log('array', array)
return array //가격[300, 1500]의 형태
}
const handleFilters = (filters, category) => {//체크된것들에 변화가 있으면 새로 받아오는 것(자식으로 부터 변화 받아오는 함수 )
//체크가 된다면 배열 안에 체크된 인덱스 번호가 추가될 것이다.
const newFilters = { ...Filters }//새로운 필터만들고
newFilters[category] = filters//해당카테고리의 필터는 입력된 filters가 될 것이다.
if (category === "price") {//필터카테고리가 가격이라면
let priceValues = handlePrice(filters)
newFilters[category] = priceValues//가격범위 지정
}
console.log(newFilters)
//필터링한 결과를 세팅해준다.
showFilteredResults(newFilters)
//필터의 상태를 새로운 필터로 바꿔준다.
setFilters(newFilters)
}
가격조정이라면 route가 다르게 행동하도록 다음과 같이 수정해준다.
for (let key in req.body.filters) { //필터에 따라서
if (req.body.filters[key].length > 0) {
if (key === "price") {
findArgs[key] = {
$gte: req.body.filters[key][0],//greater than equal
$lte: req.body.filters[key][1]//lower than equal
}
} else {//키가 price가 아니라면
findArgs[key] = req.body.filters[key];//findArgs[continents] = [인덱스 배열]의 형태
}
}
}