- client
언어검색
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2022 FE 데브매칭</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="App">
</div>
<script src="./index.js" type="module"></script>
</body>
</html>
App.js
import { fetchLanguages } from "./api.js"
import SearchInput from "./components/SearchInput.js"
import SelectedLanguages from "./components/SelectedLanguages.js"
import Suggestion from "./components/Suggestion.js"
export default function App({ $target }) {
this.state = {
fetchedLanguages : [],
selectedLanguages: []
}
this.setState = (nextState) => {
this.state = {
...this.state,
...nextState
}
suggestion.setState({
selectedIndex: 0,
items: this.state.fetchedLanguages
})
selectedLanguages.setState(this.state.selectedLanguages)
}
const selectedLanguages = new SelectedLanguages({
$target,
initialState: []
})
const searchInput = new SearchInput({
$target,
initialState: '',
onChange: async(keyword) => {
// input에 입력한 키워드가 없을 경우
if(keyword.length === 0) {
this.setState({
fetchedLanguages: [],
})
}
// input에 입력했을 경우
else {
const languages = await fetchLanguages(keyword)
console.log(languages);
this.setState({
fetchedLanguages: languages.langArr
})
}
}
})
// 현재 선택한 항목을 알기위하여 cursor 추가
const suggestion = new Suggestion({
$target,
initialState: {
cursor: 0,
items: []
},
onSelect: (language) => {
alert(language)
// 이미 선택된 언어의 경우 맨 뒤로 보내버리는 처리
const nextSelectedLanguage = [...this.state.selectedLanguages]
// 선택된 언어 배열에 클릭한 값이 있으면 그 값의 index 값을 반환, 없으면 -1을 반환
// ['java', 'javascript', 'java']
const index = nextSelectedLanguage.findIndex((selected) => selected === language)
if (index > -1) {
// splice(index, num)
nextSelectedLanguage.splice(index, 1)
}
nextSelectedLanguage.push(language)
this.setState({
...this.state,
selectedLanguages: nextSelectedLanguage
})
}
})
}
api.js
export const API_END_POINT = 'http://localhost:3002';
const request = async(url) => {
const res = await fetch(url)
if(res.ok) {
const json = await res.json();
return json;
}
throw new Error('요청에 실패함')
}
export const fetchLanguages = async (keyword) => request(`${API_END_POINT}/languages?keyword=${keyword}`)
index.js
import App from "./App.js";
new App({ $target: document.querySelector('#App')})
components/SearchInput.js
export default function SearchInput({
$target,
initialState,
onChange
}){
this.$element = document.createElement('form')
this.$element.className = "SearchInput"
this.state = initialState
$target.appendChild(this.$element)
this.render = () => {
this.$element.innerHTML = `
<input class="SearchInput__input" type="text"
placeholder="프로그램 언어를 입력하세요" value="${this.state}"/>
`
}
this.render();
// 이벤트 구현
this.$element.addEventListener('keyup', (e)=> {
const actionIgnoreKeys = ['Enter', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
if(!actionIgnoreKeys.includes(e.key)) {
console.log(e.target.value)
onChange(e.target.value)
}
})
// submit 이벤트 처리하기
this.$element.addEventListener('submit', (e) =>{
e.preventDefault();
})
}
components/Suggestion.js
export default function Suggestion({
$target,
initialState,
onSelect
}) {
this.$element = document.createElement('div');
this.$element.className = 'Suggestion';
$target.appendChild(this.$element)
// this.state = initialState;
// 현재키가 어디를 순회하는지 지정하는 selectedIndex를 추가
this.state = {
selectedIndex: 0,
items: initialState.items
}
this.setState = (nextState) => {
this.state = nextState;
this.render();
}
this.render = () => {
const { items = [], selectedIndex } = this.state;
if(items.length > 0) {
this.$element.style.display = 'block'
this.$element.innerHTML = `
<ul>
${items.map((item,index)=>`
<li class="${index===selectedIndex ? 'Suggestion__item--selected' : ''}" data-index='${index}'>${item}</li>
`).join('')}
</ul>
`
} else {
this.$element.style.display = 'none';
this.$element.innerHTML = '';
}
}
this.render();
// 화살표 위, 아래 입력으로 selectedIndex 변경하기
window.addEventListener('keyup', (e)=> {
// 검색된 항목의 배열의 길이가 0보다 클 때
if(this.state.items.length > 0) {
// 현재 selectedIndex값을 selectedIndex에 할당
const { selectedIndex } = this.state;
// 검색된 배열의 마지막 인덱스를 lastIndex에 할당
const lastIndex = this.state.items.length - 1
const navigationKeys = ['ArrowUp', 'ArrowDown']
let nextIndex = selectedIndex;
// 클릭한 키 값이 navigationKeys배열에 있으면
if(navigationKeys.includes(e.key)) {
if(e.key === 'ArrowUp') {
nextIndex = selectedIndex === 0 ? lastIndex : nextIndex - 1
} else if (e.key ==='ArrowDown') {
nextIndex = selectedIndex === lastIndex ? 0 : nextIndex + 1
}
this.setState({
...this.state,
selectedIndex: nextIndex
})
}
// 현재 커서의 위치의 검색어를 파라메터로 전달
else if(e.key === 'Enter') {
onSelect(this.state.items[this.state.selectedIndex])
}
}
})
// 마우스클릭 이벤트
this.$element.addEventListener('click', (e)=> {
const $li = e.target.closest('li');
console.log(e);
if ($li) {
const { index } = $li.dataset;
try {
onSelect(this.state.items[parseInt(index)])
} catch(e){
alert('선택할 수 없습니다.')
}
}
})
}
components/SelectedLanguages.js
export default function SelectedLanguages({
$target,
initialState
}) {
this.$element = document.createElement('div')
this.$element.className = 'SelectedLanguage';
this.state = initialState;
$target.appendChild(this.$element);
this.setState = (nextState) => {
this.state = nextState;
this.render();
}
this.render = () => {
this.$element.innerHTML = `
<ul>
${this.state.map((item) => `
<li>${item}</li>
`).join("")}
</ul>
`
}
this.render();
}
- server
EXAMSERVER
npm init
npm install express
npm install cors
const express = require('express');
const cors = require('cors');
const app = express();
const port = 3002;
let langArr = [
'Javascript',
'java',
'typescript',
'php',
'Asp',
'Jsp',
'React',
'Python',
'nodejs',
'AnelScript',
'CobolScript',
'json',
'jsonjava'
]
app.use(express.json());
app.use(cors());
app.get('/languages', function(req, res){
let langArr2 = langArr.filter(r=> r.toLowerCase().includes(req.query.keyword))
res.send({ langArr: langArr2 })
})
app.listen(port, ()=> {
console.log('연습용 서버가 돌아가고 있습니다.')
})
'Stack > JavaScript' 카테고리의 다른 글
[JS] chart.js (1) | 2022.09.01 |
---|---|
[JS] fusionChart (1) | 2022.09.01 |
[JS] 생성자 함수 복습 (0) | 2022.08.22 |
[JS] Promise / Async / 에러 핸들링 복습 (0) | 2022.06.30 |
[JS] Wave (0) | 2022.06.20 |